diff options
author | Andy Walls <awalls@radix.net> | 2009-09-26 22:17:30 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-12-05 15:40:16 -0500 |
commit | 29f8a0a50ac32ac4bc1937dcfdf8de6c406a5f10 (patch) | |
tree | 8a7406ebbb15dd7743543901eaeafc00708d31c5 /drivers/media/video/cx23885/cx23888-ir.c | |
parent | 74618244003a5a9e11240af8c5795ae747d9a2e0 (diff) |
V4L/DVB (13086): cx23885: Add skeleton v4l2_subdev for the CX23888 integrated IR controller
This change adds a skeletal implementation of a v4l2_subdevice to provide
encapsulation and abstraction of the CX23888's integrated consumer infrared
controller. This change also instantiates the cx23888_ir subdev for the
HVR-1850 which has IR hardware physically wired up to a CX23888.
The cx23888_ir subdev code is being written with long-term objectives to:
1. port it to the cx25840 module for the CX2584x, CX2583x, CX23885, & CX231xx
IR controllers
2. possibly port it to the cx18 module for the CX23418 IR controller
3. have the IR subdevice accessed abstractly in the cx23885 module, so the
driver can ignore the difference between the CX23885 and CX23888.
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx23885/cx23888-ir.c')
-rw-r--r-- | drivers/media/video/cx23885/cx23888-ir.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c new file mode 100644 index 000000000000..12df0e3a2354 --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.c | |||
@@ -0,0 +1,233 @@ | |||
1 | /* | ||
2 | * Driver for the Conexant CX23885/7/8 PCIe bridge | ||
3 | * | ||
4 | * CX23888 Integrated Consumer Infrared Controller | ||
5 | * | ||
6 | * Copyright (C) 2009 Andy Walls <awalls@radix.net> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version 2 | ||
11 | * of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | */ | ||
23 | |||
24 | #include <media/v4l2-device.h> | ||
25 | #include <media/v4l2-chip-ident.h> | ||
26 | |||
27 | #include "cx23885.h" | ||
28 | |||
29 | #define CX23888_IR_REG_BASE 0x170000 | ||
30 | /* | ||
31 | * These CX23888 register offsets have a straightforward one to one mapping | ||
32 | * to the CX23885 register offsets of 0x200 through 0x218 | ||
33 | */ | ||
34 | #define CX23888_IR_CNTRL_REG 0x170000 | ||
35 | #define CX23888_IR_TXCLK_REG 0x170004 | ||
36 | #define CX23888_IR_RXCLK_REG 0x170008 | ||
37 | #define CX23888_IR_CDUTY_REG 0x17000C | ||
38 | #define CX23888_IR_STATS_REG 0x170010 | ||
39 | #define CX23888_IR_IRQEN_REG 0x170014 | ||
40 | #define CX23888_IR_FILTR_REG 0x170018 | ||
41 | /* This register doesn't follow the pattern; it's 0x23C on a CX23885 */ | ||
42 | #define CX23888_IR_FIFO_REG 0x170040 | ||
43 | |||
44 | /* CX23888 unique registers */ | ||
45 | #define CX23888_IR_SEEDP_REG 0x17001C | ||
46 | #define CX23888_IR_TIMOL_REG 0x170020 | ||
47 | #define CX23888_IR_WAKE0_REG 0x170024 | ||
48 | #define CX23888_IR_WAKE1_REG 0x170028 | ||
49 | #define CX23888_IR_WAKE2_REG 0x17002C | ||
50 | #define CX23888_IR_MASK0_REG 0x170030 | ||
51 | #define CX23888_IR_MASK1_REG 0x170034 | ||
52 | #define CX23888_IR_MAKS2_REG 0x170038 | ||
53 | #define CX23888_IR_DPIPG_REG 0x17003C | ||
54 | #define CX23888_IR_LEARN_REG 0x170044 | ||
55 | |||
56 | |||
57 | struct cx23888_ir_state { | ||
58 | struct v4l2_subdev sd; | ||
59 | struct cx23885_dev *dev; | ||
60 | u32 id; | ||
61 | u32 rev; | ||
62 | }; | ||
63 | |||
64 | static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd) | ||
65 | { | ||
66 | return v4l2_get_subdevdata(sd); | ||
67 | } | ||
68 | |||
69 | static int cx23888_ir_write(struct cx23885_dev *dev, u32 addr, u8 value) | ||
70 | { | ||
71 | u32 reg = (addr & ~3); | ||
72 | int shift = (addr & 3) * 8; | ||
73 | u32 x = cx_read(reg); | ||
74 | |||
75 | x = (x & ~(0xff << shift)) | ((u32)value << shift); | ||
76 | cx_write(reg, x); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static | ||
81 | inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value) | ||
82 | { | ||
83 | cx_write(addr, value); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static u8 cx23888_ir_read(struct cx23885_dev *dev, u32 addr) | ||
88 | { | ||
89 | u32 x = cx_read((addr & ~3)); | ||
90 | int shift = (addr & 3) * 8; | ||
91 | |||
92 | return (x >> shift) & 0xff; | ||
93 | } | ||
94 | |||
95 | static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr) | ||
96 | { | ||
97 | return cx_read(addr); | ||
98 | } | ||
99 | |||
100 | static int cx23888_ir_and_or(struct cx23885_dev *dev, u32 addr, | ||
101 | unsigned and_mask, u8 or_value) | ||
102 | { | ||
103 | return cx23888_ir_write(dev, addr, | ||
104 | (cx23888_ir_read(dev, addr) & and_mask) | | ||
105 | or_value); | ||
106 | } | ||
107 | |||
108 | static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr, | ||
109 | u32 and_mask, u32 or_value) | ||
110 | { | ||
111 | cx_andor(addr, and_mask, or_value); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int cx23888_ir_log_status(struct v4l2_subdev *sd) | ||
116 | { | ||
117 | struct cx23888_ir_state *state = to_state(sd); | ||
118 | struct cx23885_dev *dev = state->dev; | ||
119 | u8 cntrl = cx23888_ir_read(dev, CX23888_IR_CNTRL_REG+1); | ||
120 | v4l2_info(sd, "receiver %sabled\n", cntrl & 0x1 ? "en" : "dis"); | ||
121 | v4l2_info(sd, "transmitter %sabled\n", cntrl & 0x2 ? "en" : "dis"); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match) | ||
126 | { | ||
127 | return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2; | ||
128 | } | ||
129 | |||
130 | static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd, | ||
131 | struct v4l2_dbg_chip_ident *chip) | ||
132 | { | ||
133 | struct cx23888_ir_state *state = to_state(sd); | ||
134 | |||
135 | if (cx23888_ir_dbg_match(&chip->match)) { | ||
136 | chip->ident = state->id; | ||
137 | chip->revision = state->rev; | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
143 | static int cx23888_ir_g_register(struct v4l2_subdev *sd, | ||
144 | struct v4l2_dbg_register *reg) | ||
145 | { | ||
146 | struct cx23888_ir_state *state = to_state(sd); | ||
147 | u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; | ||
148 | |||
149 | if (!cx23888_ir_dbg_match(®->match)) | ||
150 | return -EINVAL; | ||
151 | if ((addr & 0x3) != 0) | ||
152 | return -EINVAL; | ||
153 | if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) | ||
154 | return -EINVAL; | ||
155 | if (!capable(CAP_SYS_ADMIN)) | ||
156 | return -EPERM; | ||
157 | reg->size = 4; | ||
158 | reg->val = cx23888_ir_read4(state->dev, addr); | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int cx23888_ir_s_register(struct v4l2_subdev *sd, | ||
163 | struct v4l2_dbg_register *reg) | ||
164 | { | ||
165 | struct cx23888_ir_state *state = to_state(sd); | ||
166 | u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; | ||
167 | |||
168 | if (!cx23888_ir_dbg_match(®->match)) | ||
169 | return -EINVAL; | ||
170 | if ((addr & 0x3) != 0) | ||
171 | return -EINVAL; | ||
172 | if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) | ||
173 | return -EINVAL; | ||
174 | if (!capable(CAP_SYS_ADMIN)) | ||
175 | return -EPERM; | ||
176 | cx23888_ir_write4(state->dev, addr, reg->val); | ||
177 | return 0; | ||
178 | } | ||
179 | #endif | ||
180 | |||
181 | static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = { | ||
182 | .g_chip_ident = cx23888_ir_g_chip_ident, | ||
183 | .log_status = cx23888_ir_log_status, | ||
184 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
185 | .g_register = cx23888_ir_g_register, | ||
186 | .s_register = cx23888_ir_s_register, | ||
187 | #endif | ||
188 | }; | ||
189 | |||
190 | static const struct v4l2_subdev_ops cx23888_ir_controller_ops = { | ||
191 | .core = &cx23888_ir_core_ops, | ||
192 | }; | ||
193 | |||
194 | int cx23888_ir_probe(struct cx23885_dev *dev) | ||
195 | { | ||
196 | struct cx23888_ir_state *state; | ||
197 | struct v4l2_subdev *sd; | ||
198 | |||
199 | state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL); | ||
200 | if (state == NULL) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | state->dev = dev; | ||
204 | state->id = V4L2_IDENT_CX23888_IR; | ||
205 | state->rev = 0; | ||
206 | sd = &state->sd; | ||
207 | |||
208 | v4l2_subdev_init(sd, &cx23888_ir_controller_ops); | ||
209 | v4l2_set_subdevdata(sd, state); | ||
210 | /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */ | ||
211 | snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name); | ||
212 | sd->grp_id = CX23885_HW_888_IR; | ||
213 | return v4l2_device_register_subdev(&dev->v4l2_dev, sd); | ||
214 | } | ||
215 | |||
216 | int cx23888_ir_remove(struct cx23885_dev *dev) | ||
217 | { | ||
218 | struct v4l2_subdev *sd; | ||
219 | struct cx23888_ir_state *state; | ||
220 | |||
221 | sd = cx23885_find_hw(dev, CX23885_HW_888_IR); | ||
222 | if (sd == NULL) | ||
223 | return -ENODEV; | ||
224 | |||
225 | /* Disable receiver and transmitter */ | ||
226 | cx23888_ir_and_or(dev, CX23888_IR_CNTRL_REG+1, 0xfc, 0); | ||
227 | |||
228 | state = to_state(sd); | ||
229 | v4l2_device_unregister_subdev(sd); | ||
230 | kfree(state); | ||
231 | /* Nothing more to free() as state held the actual v4l2_subdev object */ | ||
232 | return 0; | ||
233 | } | ||