blob: b954195cfbe7bbc02ccab00d478ecc50d22f6b8e [file] [log] [blame]
Chaithrika U S40199c52009-05-07 09:29:25 -03001/*
Lad, Prabhakar88da0182013-02-18 07:56:41 -03002 * ths7303/53- THS7303/53 Video Amplifier driver
Chaithrika U S40199c52009-05-07 09:29:25 -03003 *
4 * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
Lad, Prabhakar88da0182013-02-18 07:56:41 -03005 * 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>
Chaithrika U S40199c52009-05-07 09:29:25 -030013 *
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
Chaithrika U S40199c52009-05-07 09:29:25 -030024#include <linux/i2c.h>
Chaithrika U S40199c52009-05-07 09:29:25 -030025#include <linux/module.h>
Lad, Prabhakar88da0182013-02-18 07:56:41 -030026#include <linux/slab.h>
Chaithrika U S40199c52009-05-07 09:29:25 -030027
Lad, Prabhakar88da0182013-02-18 07:56:41 -030028#include <media/ths7303.h>
Chaithrika U S40199c52009-05-07 09:29:25 -030029#include <media/v4l2-chip-ident.h>
Lad, Prabhakar88da0182013-02-18 07:56:41 -030030#include <media/v4l2-device.h>
Chaithrika U S40199c52009-05-07 09:29:25 -030031
Manjunath Hadliad7dcb32012-10-01 11:46:35 -030032#define THS7303_CHANNEL_1 1
33#define THS7303_CHANNEL_2 2
34#define THS7303_CHANNEL_3 3
35
Lad, Prabhakar88da0182013-02-18 07:56:41 -030036struct ths7303_state {
37 struct v4l2_subdev sd;
Lad, Prabhakardd8c3932013-05-25 13:39:36 -030038 const struct ths7303_platform_data *pdata;
Lad, Prabhakar88da0182013-02-18 07:56:41 -030039 struct v4l2_bt_timings bt;
40 int std_id;
41 int stream_on;
42 int driver_data;
43};
44
Manjunath Hadliad7dcb32012-10-01 11:46:35 -030045enum ths7303_filter_mode {
46 THS7303_FILTER_MODE_480I_576I,
47 THS7303_FILTER_MODE_480P_576P,
48 THS7303_FILTER_MODE_720P_1080I,
49 THS7303_FILTER_MODE_1080P,
50 THS7303_FILTER_MODE_DISABLE
51};
52
Chaithrika U S40199c52009-05-07 09:29:25 -030053MODULE_DESCRIPTION("TI THS7303 video amplifier driver");
54MODULE_AUTHOR("Chaithrika U S");
55MODULE_LICENSE("GPL");
56
57static int debug;
58module_param(debug, int, 0644);
59MODULE_PARM_DESC(debug, "Debug level 0-1");
60
Lad, Prabhakar88da0182013-02-18 07:56:41 -030061static inline struct ths7303_state *to_state(struct v4l2_subdev *sd)
62{
63 return container_of(sd, struct ths7303_state, sd);
64}
65
66static int ths7303_read(struct v4l2_subdev *sd, u8 reg)
67{
68 struct i2c_client *client = v4l2_get_subdevdata(sd);
69
70 return i2c_smbus_read_byte_data(client, reg);
71}
72
73static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val)
74{
75 struct i2c_client *client = v4l2_get_subdevdata(sd);
76 int ret;
77 int i;
78
79 for (i = 0; i < 3; i++) {
80 ret = i2c_smbus_write_byte_data(client, reg, val);
81 if (ret == 0)
82 return 0;
83 }
84 return ret;
85}
86
Chaithrika U S40199c52009-05-07 09:29:25 -030087/* following function is used to set ths7303 */
Manjunath Hadliad7dcb32012-10-01 11:46:35 -030088int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode)
Chaithrika U S40199c52009-05-07 09:29:25 -030089{
Manjunath Hadliad7dcb32012-10-01 11:46:35 -030090 struct i2c_client *client = v4l2_get_subdevdata(sd);
Lad, Prabhakar88da0182013-02-18 07:56:41 -030091 struct ths7303_state *state = to_state(sd);
Lad, Prabhakardd8c3932013-05-25 13:39:36 -030092 const struct ths7303_platform_data *pdata = state->pdata;
Lad, Prabhakar88da0182013-02-18 07:56:41 -030093 u8 val, sel = 0;
94 int err, disable = 0;
Chaithrika U S40199c52009-05-07 09:29:25 -030095
Manjunath Hadliad7dcb32012-10-01 11:46:35 -030096 if (!client)
97 return -EINVAL;
98
99 switch (mode) {
100 case THS7303_FILTER_MODE_1080P:
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300101 sel = 0x3; /*1080p and SXGA/UXGA */
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300102 break;
103 case THS7303_FILTER_MODE_720P_1080I:
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300104 sel = 0x2; /*720p, 1080i and SVGA/XGA */
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300105 break;
106 case THS7303_FILTER_MODE_480P_576P:
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300107 sel = 0x1; /* EDTV 480p/576p and VGA */
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300108 break;
109 case THS7303_FILTER_MODE_480I_576I:
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300110 sel = 0x0; /* SDTV, S-Video, 480i/576i */
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300111 break;
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300112 default:
113 /* disable all channels */
114 disable = 1;
Chaithrika U S40199c52009-05-07 09:29:25 -0300115 }
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300116
117 val = (sel << 6) | (sel << 3);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300118 if (!disable)
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300119 val |= (pdata->ch_1 & 0x27);
120 err = ths7303_write(sd, THS7303_CHANNEL_1, val);
Chaithrika U S40199c52009-05-07 09:29:25 -0300121 if (err)
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300122 goto out;
Chaithrika U S40199c52009-05-07 09:29:25 -0300123
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300124 val = (sel << 6) | (sel << 3);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300125 if (!disable)
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300126 val |= (pdata->ch_2 & 0x27);
127 err = ths7303_write(sd, THS7303_CHANNEL_2, val);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300128 if (err)
129 goto out;
130
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300131 val = (sel << 6) | (sel << 3);
132 if (!disable)
133 val |= (pdata->ch_3 & 0x27);
134 err = ths7303_write(sd, THS7303_CHANNEL_3, val);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300135 if (err)
136 goto out;
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300137
138 return 0;
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300139out:
140 pr_info("write byte data failed\n");
Chaithrika U S40199c52009-05-07 09:29:25 -0300141 return err;
142}
143
144static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
145{
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300146 struct ths7303_state *state = to_state(sd);
147
148 if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) {
149 state->std_id = 1;
150 state->bt.pixelclock = 0;
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300151 return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300152 }
153
154 return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);
155}
156
157static int ths7303_config(struct v4l2_subdev *sd)
158{
159 struct ths7303_state *state = to_state(sd);
160 int res;
161
162 if (!state->stream_on) {
163 ths7303_write(sd, THS7303_CHANNEL_1,
164 (ths7303_read(sd, THS7303_CHANNEL_1) & 0xf8) |
165 0x00);
166 ths7303_write(sd, THS7303_CHANNEL_2,
167 (ths7303_read(sd, THS7303_CHANNEL_2) & 0xf8) |
168 0x00);
169 ths7303_write(sd, THS7303_CHANNEL_3,
170 (ths7303_read(sd, THS7303_CHANNEL_3) & 0xf8) |
171 0x00);
172 return 0;
173 }
174
175 if (state->bt.pixelclock > 120000000)
176 res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P);
177 else if (state->bt.pixelclock > 70000000)
178 res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I);
179 else if (state->bt.pixelclock > 20000000)
180 res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P);
181 else if (state->std_id)
182 res = ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300183 else
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300184 /* disable all channels */
185 res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);
186
187 return res;
188
189}
190
191static int ths7303_s_stream(struct v4l2_subdev *sd, int enable)
192{
193 struct ths7303_state *state = to_state(sd);
194
195 state->stream_on = enable;
196
197 return ths7303_config(sd);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300198}
199
200/* for setting filter for HD output */
201static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
202 struct v4l2_dv_timings *dv_timings)
203{
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300204 struct ths7303_state *state = to_state(sd);
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300205
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300206 if (!dv_timings || dv_timings->type != V4L2_DV_BT_656_1120)
207 return -EINVAL;
Manjunath Hadliad7dcb32012-10-01 11:46:35 -0300208
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300209 state->bt = dv_timings->bt;
210 state->std_id = 0;
211
212 return ths7303_config(sd);
Chaithrika U S40199c52009-05-07 09:29:25 -0300213}
214
215static int ths7303_g_chip_ident(struct v4l2_subdev *sd,
216 struct v4l2_dbg_chip_ident *chip)
217{
218 struct i2c_client *client = v4l2_get_subdevdata(sd);
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300219 struct ths7303_state *state = to_state(sd);
Chaithrika U S40199c52009-05-07 09:29:25 -0300220
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300221 return v4l2_chip_ident_i2c_client(client, chip, state->driver_data, 0);
Chaithrika U S40199c52009-05-07 09:29:25 -0300222}
223
224static const struct v4l2_subdev_video_ops ths7303_video_ops = {
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300225 .s_stream = ths7303_s_stream,
Chaithrika U S40199c52009-05-07 09:29:25 -0300226 .s_std_output = ths7303_s_std_output,
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300227 .s_dv_timings = ths7303_s_dv_timings,
Chaithrika U S40199c52009-05-07 09:29:25 -0300228};
229
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300230#ifdef CONFIG_VIDEO_ADV_DEBUG
231
232static int ths7303_g_register(struct v4l2_subdev *sd,
233 struct v4l2_dbg_register *reg)
234{
235 struct i2c_client *client = v4l2_get_subdevdata(sd);
236
237 if (!v4l2_chip_match_i2c_client(client, &reg->match))
238 return -EINVAL;
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300239
240 reg->size = 1;
241 reg->val = ths7303_read(sd, reg->reg);
242 return 0;
243}
244
245static int ths7303_s_register(struct v4l2_subdev *sd,
Hans Verkuil977ba3b2013-03-24 08:28:46 -0300246 const struct v4l2_dbg_register *reg)
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300247{
248 struct i2c_client *client = v4l2_get_subdevdata(sd);
249
250 if (!v4l2_chip_match_i2c_client(client, &reg->match))
251 return -EINVAL;
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300252
253 ths7303_write(sd, reg->reg, reg->val);
254 return 0;
255}
256#endif
257
258static const char * const stc_lpf_sel_txt[4] = {
259 "500-kHz Filter",
260 "2.5-MHz Filter",
261 "5-MHz Filter",
262 "5-MHz Filter",
263};
264
265static const char * const in_mux_sel_txt[2] = {
266 "Input A Select",
267 "Input B Select",
268};
269
270static const char * const lpf_freq_sel_txt[4] = {
271 "9-MHz LPF",
272 "16-MHz LPF",
273 "35-MHz LPF",
274 "Bypass LPF",
275};
276
277static const char * const in_bias_sel_dis_cont_txt[8] = {
278 "Disable Channel",
279 "Mute Function - No Output",
280 "DC Bias Select",
281 "DC Bias + 250 mV Offset Select",
282 "AC Bias Select",
283 "Sync Tip Clamp with low bias",
284 "Sync Tip Clamp with mid bias",
285 "Sync Tip Clamp with high bias",
286};
287
288static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg)
289{
290 u8 val = ths7303_read(sd, reg);
291
292 if ((val & 0x7) == 0) {
293 v4l2_info(sd, "Channel %d Off\n", reg);
294 return;
295 }
296
297 v4l2_info(sd, "Channel %d On\n", reg);
298 v4l2_info(sd, " value 0x%x\n", val);
299 v4l2_info(sd, " %s\n", stc_lpf_sel_txt[(val >> 6) & 0x3]);
300 v4l2_info(sd, " %s\n", in_mux_sel_txt[(val >> 5) & 0x1]);
301 v4l2_info(sd, " %s\n", lpf_freq_sel_txt[(val >> 3) & 0x3]);
302 v4l2_info(sd, " %s\n", in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]);
303}
304
305static int ths7303_log_status(struct v4l2_subdev *sd)
306{
307 struct ths7303_state *state = to_state(sd);
308
309 v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off");
310
311 if (state->bt.pixelclock) {
312 struct v4l2_bt_timings *bt = bt = &state->bt;
313 u32 frame_width, frame_height;
314
315 frame_width = bt->width + bt->hfrontporch +
316 bt->hsync + bt->hbackporch;
317 frame_height = bt->height + bt->vfrontporch +
318 bt->vsync + bt->vbackporch;
319 v4l2_info(sd,
320 "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n",
321 bt->width, bt->height, bt->interlaced ? "i" : "p",
322 (frame_height * frame_width) > 0 ?
323 (int)bt->pixelclock /
324 (frame_height * frame_width) : 0,
325 frame_width, frame_height,
326 (int)bt->pixelclock, bt->polarities);
327 } else {
328 v4l2_info(sd, "no timings set\n");
329 }
330
331 ths7303_log_channel_status(sd, THS7303_CHANNEL_1);
332 ths7303_log_channel_status(sd, THS7303_CHANNEL_2);
333 ths7303_log_channel_status(sd, THS7303_CHANNEL_3);
334
335 return 0;
336}
337
Chaithrika U S40199c52009-05-07 09:29:25 -0300338static const struct v4l2_subdev_core_ops ths7303_core_ops = {
339 .g_chip_ident = ths7303_g_chip_ident,
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300340 .log_status = ths7303_log_status,
341#ifdef CONFIG_VIDEO_ADV_DEBUG
342 .g_register = ths7303_g_register,
343 .s_register = ths7303_s_register,
344#endif
Chaithrika U S40199c52009-05-07 09:29:25 -0300345};
346
347static const struct v4l2_subdev_ops ths7303_ops = {
348 .core = &ths7303_core_ops,
349 .video = &ths7303_video_ops,
350};
351
352static int ths7303_probe(struct i2c_client *client,
353 const struct i2c_device_id *id)
354{
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300355 struct ths7303_platform_data *pdata = client->dev.platform_data;
356 struct ths7303_state *state;
Chaithrika U S40199c52009-05-07 09:29:25 -0300357 struct v4l2_subdev *sd;
Chaithrika U S40199c52009-05-07 09:29:25 -0300358
Lad, Prabhakardd8c3932013-05-25 13:39:36 -0300359 if (pdata == NULL) {
360 dev_err(&client->dev, "No platform data\n");
361 return -EINVAL;
362 }
363
Chaithrika U S40199c52009-05-07 09:29:25 -0300364 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
365 return -ENODEV;
366
367 v4l_info(client, "chip found @ 0x%x (%s)\n",
368 client->addr << 1, client->adapter->name);
369
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300370 state = devm_kzalloc(&client->dev, sizeof(struct ths7303_state),
371 GFP_KERNEL);
372 if (!state)
Chaithrika U S40199c52009-05-07 09:29:25 -0300373 return -ENOMEM;
374
Lad, Prabhakardd8c3932013-05-25 13:39:36 -0300375 state->pdata = pdata;
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300376 sd = &state->sd;
Chaithrika U S40199c52009-05-07 09:29:25 -0300377 v4l2_i2c_subdev_init(sd, client, &ths7303_ops);
378
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300379 /* store the driver data to differntiate the chip */
380 state->driver_data = (int)id->driver_data;
381
Lad, Prabhakar8524ce52013-05-25 13:39:35 -0300382 /* set to default 480I_576I filter mode */
383 if (ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I) < 0) {
384 v4l_err(client, "Setting to 480I_576I filter mode failed!\n");
385 return -EINVAL;
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300386 }
387
388 return 0;
Chaithrika U S40199c52009-05-07 09:29:25 -0300389}
390
391static int ths7303_remove(struct i2c_client *client)
392{
393 struct v4l2_subdev *sd = i2c_get_clientdata(client);
394
395 v4l2_device_unregister_subdev(sd);
Chaithrika U S40199c52009-05-07 09:29:25 -0300396
397 return 0;
398}
399
400static const struct i2c_device_id ths7303_id[] = {
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300401 {"ths7303", V4L2_IDENT_THS7303},
402 {"ths7353", V4L2_IDENT_THS7353},
Chaithrika U S40199c52009-05-07 09:29:25 -0300403 {},
404};
405
406MODULE_DEVICE_TABLE(i2c, ths7303_id);
407
408static struct i2c_driver ths7303_driver = {
409 .driver = {
410 .owner = THIS_MODULE,
Lad, Prabhakar88da0182013-02-18 07:56:41 -0300411 .name = "ths73x3",
Chaithrika U S40199c52009-05-07 09:29:25 -0300412 },
413 .probe = ths7303_probe,
414 .remove = ths7303_remove,
415 .id_table = ths7303_id,
416};
417
Axel Linc6e8d862012-02-12 06:56:32 -0300418module_i2c_driver(ths7303_driver);