1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan |
4 | * |
5 | * 2003 by T.Adachi <tadachi@tadachi-net.com> |
6 | * 2003 by Takeru KOMORIYA <komoriya@paken.org> |
7 | * 2006 by Hans Verkuil <hverkuil@xs4all.nl> |
8 | */ |
9 | |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/videodev2.h> |
15 | #include <linux/slab.h> |
16 | #include <media/v4l2-device.h> |
17 | #include <media/i2c/upd64031a.h> |
18 | |
19 | /* --------------------- read registers functions define -------------------- */ |
20 | |
21 | /* bit masks */ |
22 | #define GR_MODE_MASK 0xc0 |
23 | #define DIRECT_3DYCS_CONNECT_MASK 0xc0 |
24 | #define SYNC_CIRCUIT_MASK 0xa0 |
25 | |
26 | /* -------------------------------------------------------------------------- */ |
27 | |
28 | MODULE_DESCRIPTION("uPD64031A driver" ); |
29 | MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil" ); |
30 | MODULE_LICENSE("GPL" ); |
31 | |
32 | static int debug; |
33 | module_param(debug, int, 0644); |
34 | |
35 | MODULE_PARM_DESC(debug, "Debug level (0-1)" ); |
36 | |
37 | |
38 | enum { |
39 | R00 = 0, R01, R02, R03, R04, |
40 | R05, R06, R07, R08, R09, |
41 | R0A, R0B, R0C, R0D, R0E, R0F, |
42 | /* unused registers |
43 | R10, R11, R12, R13, R14, |
44 | R15, R16, R17, |
45 | */ |
46 | TOT_REGS |
47 | }; |
48 | |
49 | struct upd64031a_state { |
50 | struct v4l2_subdev sd; |
51 | u8 regs[TOT_REGS]; |
52 | u8 gr_mode; |
53 | u8 direct_3dycs_connect; |
54 | u8 ext_comp_sync; |
55 | u8 ext_vert_sync; |
56 | }; |
57 | |
58 | static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) |
59 | { |
60 | return container_of(sd, struct upd64031a_state, sd); |
61 | } |
62 | |
63 | static u8 upd64031a_init[] = { |
64 | 0x00, 0xb8, 0x48, 0xd2, 0xe6, |
65 | 0x03, 0x10, 0x0b, 0xaf, 0x7f, |
66 | 0x00, 0x00, 0x1d, 0x5e, 0x00, |
67 | 0xd0 |
68 | }; |
69 | |
70 | /* ------------------------------------------------------------------------ */ |
71 | |
72 | static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) |
73 | { |
74 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
75 | u8 buf[2]; |
76 | |
77 | if (reg >= sizeof(buf)) |
78 | return 0xff; |
79 | i2c_master_recv(client, buf, count: 2); |
80 | return buf[reg]; |
81 | } |
82 | |
83 | /* ------------------------------------------------------------------------ */ |
84 | |
85 | static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) |
86 | { |
87 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
88 | u8 buf[2]; |
89 | |
90 | buf[0] = reg; |
91 | buf[1] = val; |
92 | v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n" , reg, val); |
93 | if (i2c_master_send(client, buf, count: 2) != 2) |
94 | v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n" , reg, val); |
95 | } |
96 | |
97 | /* ------------------------------------------------------------------------ */ |
98 | |
99 | /* The input changed due to new input or channel changed */ |
100 | static int upd64031a_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) |
101 | { |
102 | struct upd64031a_state *state = to_state(sd); |
103 | u8 reg = state->regs[R00]; |
104 | |
105 | v4l2_dbg(1, debug, sd, "changed input or channel\n" ); |
106 | upd64031a_write(sd, reg: R00, val: reg | 0x10); |
107 | upd64031a_write(sd, reg: R00, val: reg & ~0x10); |
108 | return 0; |
109 | } |
110 | |
111 | /* ------------------------------------------------------------------------ */ |
112 | |
113 | static int upd64031a_s_routing(struct v4l2_subdev *sd, |
114 | u32 input, u32 output, u32 config) |
115 | { |
116 | struct upd64031a_state *state = to_state(sd); |
117 | u8 r00, r05, r08; |
118 | |
119 | state->gr_mode = (input & 3) << 6; |
120 | state->direct_3dycs_connect = (input & 0xc) << 4; |
121 | state->ext_comp_sync = |
122 | (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; |
123 | state->ext_vert_sync = |
124 | (input & UPD64031A_VERTICAL_EXTERNAL) << 2; |
125 | r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; |
126 | r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | |
127 | state->ext_comp_sync | state->ext_vert_sync; |
128 | r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | |
129 | state->direct_3dycs_connect; |
130 | upd64031a_write(sd, reg: R00, val: r00); |
131 | upd64031a_write(sd, reg: R05, val: r05); |
132 | upd64031a_write(sd, reg: R08, val: r08); |
133 | return upd64031a_s_frequency(sd, NULL); |
134 | } |
135 | |
136 | static int upd64031a_log_status(struct v4l2_subdev *sd) |
137 | { |
138 | v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n" , |
139 | upd64031a_read(sd, 0), upd64031a_read(sd, 1)); |
140 | return 0; |
141 | } |
142 | |
143 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
144 | static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) |
145 | { |
146 | reg->val = upd64031a_read(sd, reg: reg->reg & 0xff); |
147 | reg->size = 1; |
148 | return 0; |
149 | } |
150 | |
151 | static int upd64031a_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) |
152 | { |
153 | upd64031a_write(sd, reg: reg->reg & 0xff, val: reg->val & 0xff); |
154 | return 0; |
155 | } |
156 | #endif |
157 | |
158 | /* ----------------------------------------------------------------------- */ |
159 | |
160 | static const struct v4l2_subdev_core_ops upd64031a_core_ops = { |
161 | .log_status = upd64031a_log_status, |
162 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
163 | .g_register = upd64031a_g_register, |
164 | .s_register = upd64031a_s_register, |
165 | #endif |
166 | }; |
167 | |
168 | static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { |
169 | .s_frequency = upd64031a_s_frequency, |
170 | }; |
171 | |
172 | static const struct v4l2_subdev_video_ops upd64031a_video_ops = { |
173 | .s_routing = upd64031a_s_routing, |
174 | }; |
175 | |
176 | static const struct v4l2_subdev_ops upd64031a_ops = { |
177 | .core = &upd64031a_core_ops, |
178 | .tuner = &upd64031a_tuner_ops, |
179 | .video = &upd64031a_video_ops, |
180 | }; |
181 | |
182 | /* ------------------------------------------------------------------------ */ |
183 | |
184 | /* i2c implementation */ |
185 | |
186 | static int upd64031a_probe(struct i2c_client *client) |
187 | { |
188 | struct upd64031a_state *state; |
189 | struct v4l2_subdev *sd; |
190 | int i; |
191 | |
192 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
193 | return -EIO; |
194 | |
195 | v4l_info(client, "chip found @ 0x%x (%s)\n" , |
196 | client->addr << 1, client->adapter->name); |
197 | |
198 | state = devm_kzalloc(dev: &client->dev, size: sizeof(*state), GFP_KERNEL); |
199 | if (state == NULL) |
200 | return -ENOMEM; |
201 | sd = &state->sd; |
202 | v4l2_i2c_subdev_init(sd, client, ops: &upd64031a_ops); |
203 | memcpy(state->regs, upd64031a_init, sizeof(state->regs)); |
204 | state->gr_mode = UPD64031A_GR_ON << 6; |
205 | state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; |
206 | state->ext_comp_sync = state->ext_vert_sync = 0; |
207 | for (i = 0; i < TOT_REGS; i++) |
208 | upd64031a_write(sd, reg: i, val: state->regs[i]); |
209 | return 0; |
210 | } |
211 | |
212 | static void upd64031a_remove(struct i2c_client *client) |
213 | { |
214 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
215 | |
216 | v4l2_device_unregister_subdev(sd); |
217 | } |
218 | |
219 | /* ----------------------------------------------------------------------- */ |
220 | |
221 | static const struct i2c_device_id upd64031a_id[] = { |
222 | { "upd64031a" , 0 }, |
223 | { } |
224 | }; |
225 | MODULE_DEVICE_TABLE(i2c, upd64031a_id); |
226 | |
227 | static struct i2c_driver upd64031a_driver = { |
228 | .driver = { |
229 | .name = "upd64031a" , |
230 | }, |
231 | .probe = upd64031a_probe, |
232 | .remove = upd64031a_remove, |
233 | .id_table = upd64031a_id, |
234 | }; |
235 | |
236 | module_i2c_driver(upd64031a_driver); |
237 | |