1 | /* |
2 | * ths7303/53- THS7303/53 Video Amplifier driver |
3 | * |
4 | * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ |
5 | * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. |
6 | * |
7 | * Author: Chaithrika U S <chaithrika@ti.com> |
8 | * |
9 | * Contributors: |
10 | * Hans Verkuil <hans.verkuil@cisco.com> |
11 | * Lad, Prabhakar <prabhakar.lad@ti.com> |
12 | * Martin Bugge <marbugge@cisco.com> |
13 | * |
14 | * This program is free software; you can redistribute it and/or |
15 | * modify it under the terms of the GNU General Public License as |
16 | * published by the Free Software Foundation version 2. |
17 | * |
18 | * This program is distributed .as is. WITHOUT ANY WARRANTY of any |
19 | * kind, whether express or implied; without even the implied warranty |
20 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 | * GNU General Public License for more details. |
22 | */ |
23 | |
24 | #include <linux/i2c.h> |
25 | #include <linux/module.h> |
26 | #include <linux/slab.h> |
27 | |
28 | #include <media/i2c/ths7303.h> |
29 | #include <media/v4l2-device.h> |
30 | |
31 | #define THS7303_CHANNEL_1 1 |
32 | #define THS7303_CHANNEL_2 2 |
33 | #define THS7303_CHANNEL_3 3 |
34 | |
35 | struct ths7303_state { |
36 | struct v4l2_subdev sd; |
37 | const struct ths7303_platform_data *pdata; |
38 | struct v4l2_bt_timings bt; |
39 | int std_id; |
40 | int stream_on; |
41 | }; |
42 | |
43 | enum ths7303_filter_mode { |
44 | THS7303_FILTER_MODE_480I_576I, |
45 | THS7303_FILTER_MODE_480P_576P, |
46 | THS7303_FILTER_MODE_720P_1080I, |
47 | THS7303_FILTER_MODE_1080P, |
48 | THS7303_FILTER_MODE_DISABLE |
49 | }; |
50 | |
51 | MODULE_DESCRIPTION("TI THS7303 video amplifier driver" ); |
52 | MODULE_AUTHOR("Chaithrika U S" ); |
53 | MODULE_LICENSE("GPL" ); |
54 | |
55 | static inline struct ths7303_state *to_state(struct v4l2_subdev *sd) |
56 | { |
57 | return container_of(sd, struct ths7303_state, sd); |
58 | } |
59 | |
60 | static int ths7303_read(struct v4l2_subdev *sd, u8 reg) |
61 | { |
62 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
63 | |
64 | return i2c_smbus_read_byte_data(client, command: reg); |
65 | } |
66 | |
67 | static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val) |
68 | { |
69 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
70 | int ret; |
71 | int i; |
72 | |
73 | for (i = 0; i < 3; i++) { |
74 | ret = i2c_smbus_write_byte_data(client, command: reg, value: val); |
75 | if (ret == 0) |
76 | return 0; |
77 | } |
78 | return ret; |
79 | } |
80 | |
81 | /* following function is used to set ths7303 */ |
82 | static int ths7303_setval(struct v4l2_subdev *sd, |
83 | enum ths7303_filter_mode mode) |
84 | { |
85 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
86 | struct ths7303_state *state = to_state(sd); |
87 | const struct ths7303_platform_data *pdata = state->pdata; |
88 | u8 val, sel = 0; |
89 | int err, disable = 0; |
90 | |
91 | if (!client) |
92 | return -EINVAL; |
93 | |
94 | switch (mode) { |
95 | case THS7303_FILTER_MODE_1080P: |
96 | sel = 0x3; /*1080p and SXGA/UXGA */ |
97 | break; |
98 | case THS7303_FILTER_MODE_720P_1080I: |
99 | sel = 0x2; /*720p, 1080i and SVGA/XGA */ |
100 | break; |
101 | case THS7303_FILTER_MODE_480P_576P: |
102 | sel = 0x1; /* EDTV 480p/576p and VGA */ |
103 | break; |
104 | case THS7303_FILTER_MODE_480I_576I: |
105 | sel = 0x0; /* SDTV, S-Video, 480i/576i */ |
106 | break; |
107 | default: |
108 | /* disable all channels */ |
109 | disable = 1; |
110 | } |
111 | |
112 | val = (sel << 6) | (sel << 3); |
113 | if (!disable) |
114 | val |= (pdata->ch_1 & 0x27); |
115 | err = ths7303_write(sd, THS7303_CHANNEL_1, val); |
116 | if (err) |
117 | goto out; |
118 | |
119 | val = (sel << 6) | (sel << 3); |
120 | if (!disable) |
121 | val |= (pdata->ch_2 & 0x27); |
122 | err = ths7303_write(sd, THS7303_CHANNEL_2, val); |
123 | if (err) |
124 | goto out; |
125 | |
126 | val = (sel << 6) | (sel << 3); |
127 | if (!disable) |
128 | val |= (pdata->ch_3 & 0x27); |
129 | err = ths7303_write(sd, THS7303_CHANNEL_3, val); |
130 | if (err) |
131 | goto out; |
132 | |
133 | return 0; |
134 | out: |
135 | pr_info("write byte data failed\n" ); |
136 | return err; |
137 | } |
138 | |
139 | static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) |
140 | { |
141 | struct ths7303_state *state = to_state(sd); |
142 | |
143 | if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { |
144 | state->std_id = 1; |
145 | state->bt.pixelclock = 0; |
146 | return ths7303_setval(sd, mode: THS7303_FILTER_MODE_480I_576I); |
147 | } |
148 | |
149 | return ths7303_setval(sd, mode: THS7303_FILTER_MODE_DISABLE); |
150 | } |
151 | |
152 | static int ths7303_config(struct v4l2_subdev *sd) |
153 | { |
154 | struct ths7303_state *state = to_state(sd); |
155 | int res; |
156 | |
157 | if (!state->stream_on) { |
158 | ths7303_write(sd, THS7303_CHANNEL_1, |
159 | val: (ths7303_read(sd, THS7303_CHANNEL_1) & 0xf8) | |
160 | 0x00); |
161 | ths7303_write(sd, THS7303_CHANNEL_2, |
162 | val: (ths7303_read(sd, THS7303_CHANNEL_2) & 0xf8) | |
163 | 0x00); |
164 | ths7303_write(sd, THS7303_CHANNEL_3, |
165 | val: (ths7303_read(sd, THS7303_CHANNEL_3) & 0xf8) | |
166 | 0x00); |
167 | return 0; |
168 | } |
169 | |
170 | if (state->bt.pixelclock > 120000000) |
171 | res = ths7303_setval(sd, mode: THS7303_FILTER_MODE_1080P); |
172 | else if (state->bt.pixelclock > 70000000) |
173 | res = ths7303_setval(sd, mode: THS7303_FILTER_MODE_720P_1080I); |
174 | else if (state->bt.pixelclock > 20000000) |
175 | res = ths7303_setval(sd, mode: THS7303_FILTER_MODE_480P_576P); |
176 | else if (state->std_id) |
177 | res = ths7303_setval(sd, mode: THS7303_FILTER_MODE_480I_576I); |
178 | else |
179 | /* disable all channels */ |
180 | res = ths7303_setval(sd, mode: THS7303_FILTER_MODE_DISABLE); |
181 | |
182 | return res; |
183 | |
184 | } |
185 | |
186 | static int ths7303_s_stream(struct v4l2_subdev *sd, int enable) |
187 | { |
188 | struct ths7303_state *state = to_state(sd); |
189 | |
190 | state->stream_on = enable; |
191 | |
192 | return ths7303_config(sd); |
193 | } |
194 | |
195 | /* for setting filter for HD output */ |
196 | static int ths7303_s_dv_timings(struct v4l2_subdev *sd, |
197 | struct v4l2_dv_timings *dv_timings) |
198 | { |
199 | struct ths7303_state *state = to_state(sd); |
200 | |
201 | if (!dv_timings || dv_timings->type != V4L2_DV_BT_656_1120) |
202 | return -EINVAL; |
203 | |
204 | state->bt = dv_timings->bt; |
205 | state->std_id = 0; |
206 | |
207 | return ths7303_config(sd); |
208 | } |
209 | |
210 | static const struct v4l2_subdev_video_ops ths7303_video_ops = { |
211 | .s_stream = ths7303_s_stream, |
212 | .s_std_output = ths7303_s_std_output, |
213 | .s_dv_timings = ths7303_s_dv_timings, |
214 | }; |
215 | |
216 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
217 | |
218 | static int ths7303_g_register(struct v4l2_subdev *sd, |
219 | struct v4l2_dbg_register *reg) |
220 | { |
221 | reg->size = 1; |
222 | reg->val = ths7303_read(sd, reg: reg->reg); |
223 | return 0; |
224 | } |
225 | |
226 | static int ths7303_s_register(struct v4l2_subdev *sd, |
227 | const struct v4l2_dbg_register *reg) |
228 | { |
229 | ths7303_write(sd, reg: reg->reg, val: reg->val); |
230 | return 0; |
231 | } |
232 | #endif |
233 | |
234 | static const char * const stc_lpf_sel_txt[4] = { |
235 | "500-kHz Filter" , |
236 | "2.5-MHz Filter" , |
237 | "5-MHz Filter" , |
238 | "5-MHz Filter" , |
239 | }; |
240 | |
241 | static const char * const in_mux_sel_txt[2] = { |
242 | "Input A Select" , |
243 | "Input B Select" , |
244 | }; |
245 | |
246 | static const char * const lpf_freq_sel_txt[4] = { |
247 | "9-MHz LPF" , |
248 | "16-MHz LPF" , |
249 | "35-MHz LPF" , |
250 | "Bypass LPF" , |
251 | }; |
252 | |
253 | static const char * const in_bias_sel_dis_cont_txt[8] = { |
254 | "Disable Channel" , |
255 | "Mute Function - No Output" , |
256 | "DC Bias Select" , |
257 | "DC Bias + 250 mV Offset Select" , |
258 | "AC Bias Select" , |
259 | "Sync Tip Clamp with low bias" , |
260 | "Sync Tip Clamp with mid bias" , |
261 | "Sync Tip Clamp with high bias" , |
262 | }; |
263 | |
264 | static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg) |
265 | { |
266 | u8 val = ths7303_read(sd, reg); |
267 | |
268 | if ((val & 0x7) == 0) { |
269 | v4l2_info(sd, "Channel %d Off\n" , reg); |
270 | return; |
271 | } |
272 | |
273 | v4l2_info(sd, "Channel %d On\n" , reg); |
274 | v4l2_info(sd, " value 0x%x\n" , val); |
275 | v4l2_info(sd, " %s\n" , stc_lpf_sel_txt[(val >> 6) & 0x3]); |
276 | v4l2_info(sd, " %s\n" , in_mux_sel_txt[(val >> 5) & 0x1]); |
277 | v4l2_info(sd, " %s\n" , lpf_freq_sel_txt[(val >> 3) & 0x3]); |
278 | v4l2_info(sd, " %s\n" , in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]); |
279 | } |
280 | |
281 | static int ths7303_log_status(struct v4l2_subdev *sd) |
282 | { |
283 | struct ths7303_state *state = to_state(sd); |
284 | |
285 | v4l2_info(sd, "stream %s\n" , state->stream_on ? "On" : "Off" ); |
286 | |
287 | if (state->bt.pixelclock) { |
288 | struct v4l2_bt_timings *bt = &state->bt; |
289 | u32 frame_width, frame_height; |
290 | |
291 | frame_width = V4L2_DV_BT_FRAME_WIDTH(bt); |
292 | frame_height = V4L2_DV_BT_FRAME_HEIGHT(bt); |
293 | v4l2_info(sd, |
294 | "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n" , |
295 | bt->width, bt->height, bt->interlaced ? "i" : "p" , |
296 | (frame_height * frame_width) > 0 ? |
297 | (int)bt->pixelclock / |
298 | (frame_height * frame_width) : 0, |
299 | frame_width, frame_height, |
300 | (int)bt->pixelclock, bt->polarities); |
301 | } else { |
302 | v4l2_info(sd, "no timings set\n" ); |
303 | } |
304 | |
305 | ths7303_log_channel_status(sd, THS7303_CHANNEL_1); |
306 | ths7303_log_channel_status(sd, THS7303_CHANNEL_2); |
307 | ths7303_log_channel_status(sd, THS7303_CHANNEL_3); |
308 | |
309 | return 0; |
310 | } |
311 | |
312 | static const struct v4l2_subdev_core_ops ths7303_core_ops = { |
313 | .log_status = ths7303_log_status, |
314 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
315 | .g_register = ths7303_g_register, |
316 | .s_register = ths7303_s_register, |
317 | #endif |
318 | }; |
319 | |
320 | static const struct v4l2_subdev_ops ths7303_ops = { |
321 | .core = &ths7303_core_ops, |
322 | .video = &ths7303_video_ops, |
323 | }; |
324 | |
325 | static int ths7303_probe(struct i2c_client *client) |
326 | { |
327 | struct ths7303_platform_data *pdata = client->dev.platform_data; |
328 | struct ths7303_state *state; |
329 | struct v4l2_subdev *sd; |
330 | |
331 | if (pdata == NULL) { |
332 | dev_err(&client->dev, "No platform data\n" ); |
333 | return -EINVAL; |
334 | } |
335 | |
336 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
337 | return -ENODEV; |
338 | |
339 | v4l_info(client, "chip found @ 0x%x (%s)\n" , |
340 | client->addr << 1, client->adapter->name); |
341 | |
342 | state = devm_kzalloc(dev: &client->dev, size: sizeof(struct ths7303_state), |
343 | GFP_KERNEL); |
344 | if (!state) |
345 | return -ENOMEM; |
346 | |
347 | state->pdata = pdata; |
348 | sd = &state->sd; |
349 | v4l2_i2c_subdev_init(sd, client, ops: &ths7303_ops); |
350 | |
351 | /* set to default 480I_576I filter mode */ |
352 | if (ths7303_setval(sd, mode: THS7303_FILTER_MODE_480I_576I) < 0) { |
353 | v4l_err(client, "Setting to 480I_576I filter mode failed!\n" ); |
354 | return -EINVAL; |
355 | } |
356 | |
357 | return 0; |
358 | } |
359 | |
360 | static void ths7303_remove(struct i2c_client *client) |
361 | { |
362 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
363 | |
364 | v4l2_device_unregister_subdev(sd); |
365 | } |
366 | |
367 | static const struct i2c_device_id ths7303_id[] = { |
368 | {"ths7303" , 0}, |
369 | {"ths7353" , 0}, |
370 | {}, |
371 | }; |
372 | |
373 | MODULE_DEVICE_TABLE(i2c, ths7303_id); |
374 | |
375 | static struct i2c_driver ths7303_driver = { |
376 | .driver = { |
377 | .name = "ths73x3" , |
378 | }, |
379 | .probe = ths7303_probe, |
380 | .remove = ths7303_remove, |
381 | .id_table = ths7303_id, |
382 | }; |
383 | |
384 | module_i2c_driver(ths7303_driver); |
385 | |