blob: a3ea3bbd16c6ec8e5f6aefa6889650a99cfba962 [file] [log] [blame]
Richard Röjfors6789cb52009-09-18 21:17:20 -03001/*
2 * adv7180.c Analog Devices ADV7180 video decoder driver
3 * Copyright (c) 2009 Intel Corporation
Vladimir Barinovcccb83f2013-05-29 14:50:57 -03004 * Copyright (C) 2013 Cogent Embedded, Inc.
5 * Copyright (C) 2013 Renesas Solutions Corp.
Richard Röjfors6789cb52009-09-18 21:17:20 -03006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/errno.h>
24#include <linux/kernel.h>
25#include <linux/interrupt.h>
26#include <linux/i2c.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090027#include <linux/slab.h>
Richard Röjfors6789cb52009-09-18 21:17:20 -030028#include <media/v4l2-ioctl.h>
29#include <linux/videodev2.h>
30#include <media/v4l2-device.h>
Federico Vagac9fbedd2012-07-11 11:29:33 -030031#include <media/v4l2-ctrls.h>
Richard Röjfors42752f72009-09-22 06:07:06 -030032#include <linux/mutex.h>
Richard Röjfors6789cb52009-09-18 21:17:20 -030033
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030034#define ADV7180_REG_INPUT_CONTROL 0x00
Richard Röjforsd3124292009-09-22 06:05:42 -030035#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
36#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
37#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20
38#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30
39#define ADV7180_INPUT_CONTROL_NTSC_J 0x40
40#define ADV7180_INPUT_CONTROL_NTSC_M 0x50
41#define ADV7180_INPUT_CONTROL_PAL60 0x60
42#define ADV7180_INPUT_CONTROL_NTSC_443 0x70
43#define ADV7180_INPUT_CONTROL_PAL_BG 0x80
44#define ADV7180_INPUT_CONTROL_PAL_N 0x90
45#define ADV7180_INPUT_CONTROL_PAL_M 0xa0
46#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0
47#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0
48#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
49#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
50#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
Federico Vagabca7ad12012-04-12 12:39:36 -030051#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f
Richard Röjforsd3124292009-09-22 06:05:42 -030052
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030053#define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x04
Richard Röjfors42752f72009-09-22 06:07:06 -030054#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
Richard Röjfors6789cb52009-09-18 21:17:20 -030055
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030056#define ADV7180_REG_AUTODETECT_ENABLE 0x07
Richard Röjfors42752f72009-09-22 06:07:06 -030057#define ADV7180_AUTODETECT_DEFAULT 0x7f
Federico Vagac9fbedd2012-07-11 11:29:33 -030058/* Contrast */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030059#define ADV7180_REG_CON 0x08 /*Unsigned */
Federico Vagac9fbedd2012-07-11 11:29:33 -030060#define ADV7180_CON_MIN 0
61#define ADV7180_CON_DEF 128
62#define ADV7180_CON_MAX 255
63/* Brightness*/
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030064#define ADV7180_REG_BRI 0x0a /*Signed */
Federico Vagac9fbedd2012-07-11 11:29:33 -030065#define ADV7180_BRI_MIN -128
66#define ADV7180_BRI_DEF 0
67#define ADV7180_BRI_MAX 127
68/* Hue */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030069#define ADV7180_REG_HUE 0x0b /*Signed, inverted */
Federico Vagac9fbedd2012-07-11 11:29:33 -030070#define ADV7180_HUE_MIN -127
71#define ADV7180_HUE_DEF 0
72#define ADV7180_HUE_MAX 128
Federico Vagabca7ad12012-04-12 12:39:36 -030073
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030074#define ADV7180_REG_CTRL 0x0e
75#define ADV7180_CTRL_IRQ_SPACE 0x20
Richard Röjfors6789cb52009-09-18 21:17:20 -030076
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030077#define ADV7180_REG_PWR_MAN 0x0f
Federico Vagabca7ad12012-04-12 12:39:36 -030078#define ADV7180_PWR_MAN_ON 0x04
79#define ADV7180_PWR_MAN_OFF 0x24
80#define ADV7180_PWR_MAN_RES 0x80
81
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030082#define ADV7180_REG_STATUS1 0x10
Richard Röjforsd3124292009-09-22 06:05:42 -030083#define ADV7180_STATUS1_IN_LOCK 0x01
84#define ADV7180_STATUS1_AUTOD_MASK 0x70
Richard Röjfors6789cb52009-09-18 21:17:20 -030085#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
86#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
87#define ADV7180_STATUS1_AUTOD_PAL_M 0x20
88#define ADV7180_STATUS1_AUTOD_PAL_60 0x30
89#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40
90#define ADV7180_STATUS1_AUTOD_SECAM 0x50
91#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60
92#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70
93
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030094#define ADV7180_REG_IDENT 0x11
Richard Röjfors6789cb52009-09-18 21:17:20 -030095#define ADV7180_ID_7180 0x18
96
Lars-Peter Clausen029d6172015-01-23 12:52:23 -030097#define ADV7180_REG_ICONF1 0x40
Richard Röjfors42752f72009-09-22 06:07:06 -030098#define ADV7180_ICONF1_ACTIVE_LOW 0x01
99#define ADV7180_ICONF1_PSYNC_ONLY 0x10
100#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
Federico Vagac9fbedd2012-07-11 11:29:33 -0300101/* Saturation */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300102#define ADV7180_REG_SD_SAT_CB 0xe3 /*Unsigned */
103#define ADV7180_REG_SD_SAT_CR 0xe4 /*Unsigned */
Federico Vagac9fbedd2012-07-11 11:29:33 -0300104#define ADV7180_SAT_MIN 0
105#define ADV7180_SAT_DEF 128
106#define ADV7180_SAT_MAX 255
Federico Vagabca7ad12012-04-12 12:39:36 -0300107
Richard Röjfors42752f72009-09-22 06:07:06 -0300108#define ADV7180_IRQ1_LOCK 0x01
109#define ADV7180_IRQ1_UNLOCK 0x02
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300110#define ADV7180_REG_ISR1 0x42
111#define ADV7180_REG_ICR1 0x43
112#define ADV7180_REG_IMR1 0x44
113#define ADV7180_REG_IMR2 0x48
Richard Röjfors42752f72009-09-22 06:07:06 -0300114#define ADV7180_IRQ3_AD_CHANGE 0x08
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300115#define ADV7180_REG_ISR3 0x4A
116#define ADV7180_REG_ICR3 0x4B
117#define ADV7180_REG_IMR3 0x4C
118#define ADV7180_REG_IMR4 0x50
Richard Röjfors6789cb52009-09-18 21:17:20 -0300119
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300120#define ADV7180_REG_NTSC_V_BIT_END 0xE6
Federico Vagabca7ad12012-04-12 12:39:36 -0300121#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
122
Richard Röjfors6789cb52009-09-18 21:17:20 -0300123struct adv7180_state {
Federico Vagac9fbedd2012-07-11 11:29:33 -0300124 struct v4l2_ctrl_handler ctrl_hdl;
Richard Röjforsc277b602009-09-22 06:06:34 -0300125 struct v4l2_subdev sd;
Richard Röjfors42752f72009-09-22 06:07:06 -0300126 struct mutex mutex; /* mutual excl. when accessing chip */
127 int irq;
Richard Röjforsc277b602009-09-22 06:06:34 -0300128 v4l2_std_id curr_norm;
129 bool autodetect;
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300130 bool powered;
Federico Vagabca7ad12012-04-12 12:39:36 -0300131 u8 input;
Richard Röjfors6789cb52009-09-18 21:17:20 -0300132};
Federico Vagac9fbedd2012-07-11 11:29:33 -0300133#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
134 struct adv7180_state, \
135 ctrl_hdl)->sd)
Richard Röjfors6789cb52009-09-18 21:17:20 -0300136
Richard Röjforsd3124292009-09-22 06:05:42 -0300137static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
Richard Röjfors6789cb52009-09-18 21:17:20 -0300138{
Vladimir Barinovb294a192013-04-11 18:06:46 -0300139 /* in case V4L2_IN_ST_NO_SIGNAL */
140 if (!(status1 & ADV7180_STATUS1_IN_LOCK))
141 return V4L2_STD_UNKNOWN;
142
Richard Röjfors6789cb52009-09-18 21:17:20 -0300143 switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
144 case ADV7180_STATUS1_AUTOD_NTSM_M_J:
Richard Röjforsd3124292009-09-22 06:05:42 -0300145 return V4L2_STD_NTSC;
Richard Röjfors6789cb52009-09-18 21:17:20 -0300146 case ADV7180_STATUS1_AUTOD_NTSC_4_43:
147 return V4L2_STD_NTSC_443;
148 case ADV7180_STATUS1_AUTOD_PAL_M:
149 return V4L2_STD_PAL_M;
150 case ADV7180_STATUS1_AUTOD_PAL_60:
151 return V4L2_STD_PAL_60;
152 case ADV7180_STATUS1_AUTOD_PAL_B_G:
153 return V4L2_STD_PAL;
154 case ADV7180_STATUS1_AUTOD_SECAM:
155 return V4L2_STD_SECAM;
156 case ADV7180_STATUS1_AUTOD_PAL_COMB:
157 return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
158 case ADV7180_STATUS1_AUTOD_SECAM_525:
159 return V4L2_STD_SECAM;
160 default:
161 return V4L2_STD_UNKNOWN;
162 }
163}
164
Richard Röjforsc277b602009-09-22 06:06:34 -0300165static int v4l2_std_to_adv7180(v4l2_std_id std)
166{
167 if (std == V4L2_STD_PAL_60)
168 return ADV7180_INPUT_CONTROL_PAL60;
169 if (std == V4L2_STD_NTSC_443)
170 return ADV7180_INPUT_CONTROL_NTSC_443;
171 if (std == V4L2_STD_PAL_N)
172 return ADV7180_INPUT_CONTROL_PAL_N;
173 if (std == V4L2_STD_PAL_M)
174 return ADV7180_INPUT_CONTROL_PAL_M;
175 if (std == V4L2_STD_PAL_Nc)
176 return ADV7180_INPUT_CONTROL_PAL_COMB_N;
177
178 if (std & V4L2_STD_PAL)
179 return ADV7180_INPUT_CONTROL_PAL_BG;
180 if (std & V4L2_STD_NTSC)
181 return ADV7180_INPUT_CONTROL_NTSC_M;
182 if (std & V4L2_STD_SECAM)
183 return ADV7180_INPUT_CONTROL_PAL_SECAM;
184
185 return -EINVAL;
186}
187
Richard Röjforsd3124292009-09-22 06:05:42 -0300188static u32 adv7180_status_to_v4l2(u8 status1)
189{
190 if (!(status1 & ADV7180_STATUS1_IN_LOCK))
191 return V4L2_IN_ST_NO_SIGNAL;
192
193 return 0;
194}
195
196static int __adv7180_status(struct i2c_client *client, u32 *status,
Federico Vagabca7ad12012-04-12 12:39:36 -0300197 v4l2_std_id *std)
Richard Röjforsd3124292009-09-22 06:05:42 -0300198{
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300199 int status1 = i2c_smbus_read_byte_data(client, ADV7180_REG_STATUS1);
Richard Röjforsd3124292009-09-22 06:05:42 -0300200
201 if (status1 < 0)
202 return status1;
203
204 if (status)
205 *status = adv7180_status_to_v4l2(status1);
206 if (std)
207 *std = adv7180_std_to_v4l2(status1);
208
209 return 0;
210}
211
Richard Röjfors6789cb52009-09-18 21:17:20 -0300212static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
213{
214 return container_of(sd, struct adv7180_state, sd);
215}
216
217static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
218{
Richard Röjforsc277b602009-09-22 06:06:34 -0300219 struct adv7180_state *state = to_state(sd);
Richard Röjfors42752f72009-09-22 06:07:06 -0300220 int err = mutex_lock_interruptible(&state->mutex);
221 if (err)
222 return err;
Richard Röjforsc277b602009-09-22 06:06:34 -0300223
Richard Röjfors42752f72009-09-22 06:07:06 -0300224 /* when we are interrupt driven we know the state */
225 if (!state->autodetect || state->irq > 0)
Richard Röjforsc277b602009-09-22 06:06:34 -0300226 *std = state->curr_norm;
227 else
228 err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
229
Richard Röjfors42752f72009-09-22 06:07:06 -0300230 mutex_unlock(&state->mutex);
Richard Röjforsc277b602009-09-22 06:06:34 -0300231 return err;
Richard Röjforsd3124292009-09-22 06:05:42 -0300232}
Richard Röjfors6789cb52009-09-18 21:17:20 -0300233
Federico Vagabca7ad12012-04-12 12:39:36 -0300234static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
235 u32 output, u32 config)
236{
237 struct adv7180_state *state = to_state(sd);
238 int ret = mutex_lock_interruptible(&state->mutex);
239 struct i2c_client *client = v4l2_get_subdevdata(sd);
240
241 if (ret)
242 return ret;
243
Federico Vagac9fbedd2012-07-11 11:29:33 -0300244 /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
Federico Vagabca7ad12012-04-12 12:39:36 -0300245 * all inputs and let the card driver take care of validation
246 */
247 if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
248 goto out;
249
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300250 ret = i2c_smbus_read_byte_data(client, ADV7180_REG_INPUT_CONTROL);
Federico Vagabca7ad12012-04-12 12:39:36 -0300251 if (ret < 0)
252 goto out;
253
254 ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
255 ret = i2c_smbus_write_byte_data(client,
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300256 ADV7180_REG_INPUT_CONTROL, ret | input);
Federico Vagabca7ad12012-04-12 12:39:36 -0300257 state->input = input;
258out:
259 mutex_unlock(&state->mutex);
260 return ret;
261}
262
Richard Röjforsd3124292009-09-22 06:05:42 -0300263static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
264{
Richard Röjfors42752f72009-09-22 06:07:06 -0300265 struct adv7180_state *state = to_state(sd);
266 int ret = mutex_lock_interruptible(&state->mutex);
267 if (ret)
268 return ret;
269
270 ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
271 mutex_unlock(&state->mutex);
272 return ret;
Richard Röjfors6789cb52009-09-18 21:17:20 -0300273}
274
Richard Röjforsc277b602009-09-22 06:06:34 -0300275static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
276{
277 struct adv7180_state *state = to_state(sd);
278 struct i2c_client *client = v4l2_get_subdevdata(sd);
Richard Röjfors42752f72009-09-22 06:07:06 -0300279 int ret = mutex_lock_interruptible(&state->mutex);
280 if (ret)
281 return ret;
Richard Röjforsc277b602009-09-22 06:06:34 -0300282
283 /* all standards -> autodetect */
284 if (std == V4L2_STD_ALL) {
Federico Vagabca7ad12012-04-12 12:39:36 -0300285 ret =
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300286 i2c_smbus_write_byte_data(client, ADV7180_REG_INPUT_CONTROL,
Federico Vagabca7ad12012-04-12 12:39:36 -0300287 ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
288 | state->input);
Richard Röjforsc277b602009-09-22 06:06:34 -0300289 if (ret < 0)
290 goto out;
291
Richard Röjfors42752f72009-09-22 06:07:06 -0300292 __adv7180_status(client, NULL, &state->curr_norm);
Richard Röjforsc277b602009-09-22 06:06:34 -0300293 state->autodetect = true;
294 } else {
295 ret = v4l2_std_to_adv7180(std);
296 if (ret < 0)
297 goto out;
298
299 ret = i2c_smbus_write_byte_data(client,
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300300 ADV7180_REG_INPUT_CONTROL,
Federico Vagabca7ad12012-04-12 12:39:36 -0300301 ret | state->input);
Richard Röjforsc277b602009-09-22 06:06:34 -0300302 if (ret < 0)
303 goto out;
304
305 state->curr_norm = std;
306 state->autodetect = false;
307 }
308 ret = 0;
309out:
Richard Röjfors42752f72009-09-22 06:07:06 -0300310 mutex_unlock(&state->mutex);
Richard Röjforsc277b602009-09-22 06:06:34 -0300311 return ret;
312}
313
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300314static int adv7180_set_power(struct adv7180_state *state,
315 struct i2c_client *client, bool on)
316{
317 u8 val;
318
319 if (on)
320 val = ADV7180_PWR_MAN_ON;
321 else
322 val = ADV7180_PWR_MAN_OFF;
323
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300324 return i2c_smbus_write_byte_data(client, ADV7180_REG_PWR_MAN, val);
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300325}
326
327static int adv7180_s_power(struct v4l2_subdev *sd, int on)
328{
329 struct adv7180_state *state = to_state(sd);
330 struct i2c_client *client = v4l2_get_subdevdata(sd);
331 int ret;
332
333 ret = mutex_lock_interruptible(&state->mutex);
334 if (ret)
335 return ret;
336
337 ret = adv7180_set_power(state, client, on);
338 if (ret == 0)
339 state->powered = on;
340
341 mutex_unlock(&state->mutex);
342 return ret;
343}
344
Federico Vagac9fbedd2012-07-11 11:29:33 -0300345static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
Federico Vagabca7ad12012-04-12 12:39:36 -0300346{
Federico Vagac9fbedd2012-07-11 11:29:33 -0300347 struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
Federico Vagabca7ad12012-04-12 12:39:36 -0300348 struct adv7180_state *state = to_state(sd);
349 struct i2c_client *client = v4l2_get_subdevdata(sd);
350 int ret = mutex_lock_interruptible(&state->mutex);
Federico Vagac9fbedd2012-07-11 11:29:33 -0300351 int val;
352
Federico Vagabca7ad12012-04-12 12:39:36 -0300353 if (ret)
354 return ret;
Federico Vagac9fbedd2012-07-11 11:29:33 -0300355 val = ctrl->val;
Federico Vagabca7ad12012-04-12 12:39:36 -0300356 switch (ctrl->id) {
357 case V4L2_CID_BRIGHTNESS:
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300358 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_BRI, val);
Federico Vagabca7ad12012-04-12 12:39:36 -0300359 break;
360 case V4L2_CID_HUE:
Federico Vagabca7ad12012-04-12 12:39:36 -0300361 /*Hue is inverted according to HSL chart */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300362 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_HUE, -val);
Federico Vagabca7ad12012-04-12 12:39:36 -0300363 break;
364 case V4L2_CID_CONTRAST:
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300365 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_CON, val);
Federico Vagabca7ad12012-04-12 12:39:36 -0300366 break;
367 case V4L2_CID_SATURATION:
Federico Vagabca7ad12012-04-12 12:39:36 -0300368 /*
369 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
370 *Let's not confuse the user, everybody understands saturation
371 */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300372 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_SD_SAT_CB,
Federico Vagac9fbedd2012-07-11 11:29:33 -0300373 val);
Federico Vagabca7ad12012-04-12 12:39:36 -0300374 if (ret < 0)
375 break;
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300376 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_SD_SAT_CR,
Federico Vagac9fbedd2012-07-11 11:29:33 -0300377 val);
Federico Vagabca7ad12012-04-12 12:39:36 -0300378 break;
379 default:
380 ret = -EINVAL;
381 }
382
383 mutex_unlock(&state->mutex);
384 return ret;
385}
386
Federico Vagac9fbedd2012-07-11 11:29:33 -0300387static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
388 .s_ctrl = adv7180_s_ctrl,
389};
390
391static int adv7180_init_controls(struct adv7180_state *state)
392{
393 v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
394
395 v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
396 V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
397 ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
398 v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
399 V4L2_CID_CONTRAST, ADV7180_CON_MIN,
400 ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
401 v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
402 V4L2_CID_SATURATION, ADV7180_SAT_MIN,
403 ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
404 v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
405 V4L2_CID_HUE, ADV7180_HUE_MIN,
406 ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
407 state->sd.ctrl_handler = &state->ctrl_hdl;
408 if (state->ctrl_hdl.error) {
409 int err = state->ctrl_hdl.error;
410
411 v4l2_ctrl_handler_free(&state->ctrl_hdl);
412 return err;
413 }
414 v4l2_ctrl_handler_setup(&state->ctrl_hdl);
415
416 return 0;
417}
418static void adv7180_exit_controls(struct adv7180_state *state)
419{
420 v4l2_ctrl_handler_free(&state->ctrl_hdl);
421}
422
Vladimir Barinovcccb83f2013-05-29 14:50:57 -0300423static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300424 u32 *code)
Vladimir Barinovcccb83f2013-05-29 14:50:57 -0300425{
426 if (index > 0)
427 return -EINVAL;
428
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300429 *code = MEDIA_BUS_FMT_YUYV8_2X8;
Vladimir Barinovcccb83f2013-05-29 14:50:57 -0300430
431 return 0;
432}
433
434static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
435 struct v4l2_mbus_framefmt *fmt)
436{
437 struct adv7180_state *state = to_state(sd);
438
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300439 fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
Vladimir Barinovcccb83f2013-05-29 14:50:57 -0300440 fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
441 fmt->field = V4L2_FIELD_INTERLACED;
442 fmt->width = 720;
443 fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
444
445 return 0;
446}
447
448static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
449 struct v4l2_mbus_config *cfg)
450{
451 /*
452 * The ADV7180 sensor supports BT.601/656 output modes.
453 * The BT.656 is default and not yet configurable by s/w.
454 */
455 cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
456 V4L2_MBUS_DATA_ACTIVE_HIGH;
457 cfg->type = V4L2_MBUS_BT656;
458
459 return 0;
460}
461
Richard Röjfors6789cb52009-09-18 21:17:20 -0300462static const struct v4l2_subdev_video_ops adv7180_video_ops = {
Laurent Pinchart8774bed2014-04-28 16:53:01 -0300463 .s_std = adv7180_s_std,
Richard Röjfors6789cb52009-09-18 21:17:20 -0300464 .querystd = adv7180_querystd,
Richard Röjforsd3124292009-09-22 06:05:42 -0300465 .g_input_status = adv7180_g_input_status,
Federico Vagabca7ad12012-04-12 12:39:36 -0300466 .s_routing = adv7180_s_routing,
Vladimir Barinovcccb83f2013-05-29 14:50:57 -0300467 .enum_mbus_fmt = adv7180_enum_mbus_fmt,
468 .try_mbus_fmt = adv7180_mbus_fmt,
469 .g_mbus_fmt = adv7180_mbus_fmt,
470 .s_mbus_fmt = adv7180_mbus_fmt,
471 .g_mbus_config = adv7180_g_mbus_config,
Richard Röjfors6789cb52009-09-18 21:17:20 -0300472};
473
474static const struct v4l2_subdev_core_ops adv7180_core_ops = {
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300475 .s_power = adv7180_s_power,
Richard Röjfors6789cb52009-09-18 21:17:20 -0300476};
477
478static const struct v4l2_subdev_ops adv7180_ops = {
479 .core = &adv7180_core_ops,
480 .video = &adv7180_video_ops,
481};
482
Lars-Peter Clausen0c255342014-03-07 13:14:31 -0300483static irqreturn_t adv7180_irq(int irq, void *devid)
Richard Röjfors42752f72009-09-22 06:07:06 -0300484{
Lars-Peter Clausen0c255342014-03-07 13:14:31 -0300485 struct adv7180_state *state = devid;
Richard Röjfors42752f72009-09-22 06:07:06 -0300486 struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
487 u8 isr3;
488
489 mutex_lock(&state->mutex);
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300490 i2c_smbus_write_byte_data(client, ADV7180_REG_CTRL,
491 ADV7180_CTRL_IRQ_SPACE);
492 isr3 = i2c_smbus_read_byte_data(client, ADV7180_REG_ISR3);
Richard Röjfors42752f72009-09-22 06:07:06 -0300493 /* clear */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300494 i2c_smbus_write_byte_data(client, ADV7180_REG_ICR3, isr3);
495 i2c_smbus_write_byte_data(client, ADV7180_REG_CTRL, 0);
Richard Röjfors42752f72009-09-22 06:07:06 -0300496
497 if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
498 __adv7180_status(client, NULL, &state->curr_norm);
499 mutex_unlock(&state->mutex);
500
Richard Röjfors42752f72009-09-22 06:07:06 -0300501 return IRQ_HANDLED;
502}
503
Federico Vagabca7ad12012-04-12 12:39:36 -0300504static int init_device(struct i2c_client *client, struct adv7180_state *state)
505{
506 int ret;
507
508 /* Initialize adv7180 */
509 /* Enable autodetection */
510 if (state->autodetect) {
511 ret =
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300512 i2c_smbus_write_byte_data(client, ADV7180_REG_INPUT_CONTROL,
Federico Vagabca7ad12012-04-12 12:39:36 -0300513 ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
514 | state->input);
515 if (ret < 0)
516 return ret;
517
518 ret =
519 i2c_smbus_write_byte_data(client,
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300520 ADV7180_REG_AUTODETECT_ENABLE,
Federico Vagabca7ad12012-04-12 12:39:36 -0300521 ADV7180_AUTODETECT_DEFAULT);
522 if (ret < 0)
523 return ret;
524 } else {
525 ret = v4l2_std_to_adv7180(state->curr_norm);
526 if (ret < 0)
527 return ret;
528
529 ret =
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300530 i2c_smbus_write_byte_data(client, ADV7180_REG_INPUT_CONTROL,
Federico Vagabca7ad12012-04-12 12:39:36 -0300531 ret | state->input);
532 if (ret < 0)
533 return ret;
534
535 }
536 /* ITU-R BT.656-4 compatible */
537 ret = i2c_smbus_write_byte_data(client,
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300538 ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
Federico Vagabca7ad12012-04-12 12:39:36 -0300539 ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
540 if (ret < 0)
541 return ret;
542
543 /* Manually set V bit end position in NTSC mode */
544 ret = i2c_smbus_write_byte_data(client,
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300545 ADV7180_REG_NTSC_V_BIT_END,
Federico Vagabca7ad12012-04-12 12:39:36 -0300546 ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
547 if (ret < 0)
548 return ret;
549
550 /* read current norm */
551 __adv7180_status(client, NULL, &state->curr_norm);
552
553 /* register for interrupts */
554 if (state->irq > 0) {
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300555 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_CTRL,
556 ADV7180_CTRL_IRQ_SPACE);
Federico Vagabca7ad12012-04-12 12:39:36 -0300557 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300558 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300559
560 /* config the Interrupt pin to be active low */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300561 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_ICONF1,
Federico Vagabca7ad12012-04-12 12:39:36 -0300562 ADV7180_ICONF1_ACTIVE_LOW |
563 ADV7180_ICONF1_PSYNC_ONLY);
564 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300565 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300566
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300567 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_IMR1, 0);
Federico Vagabca7ad12012-04-12 12:39:36 -0300568 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300569 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300570
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300571 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_IMR2, 0);
Federico Vagabca7ad12012-04-12 12:39:36 -0300572 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300573 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300574
575 /* enable AD change interrupts interrupts */
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300576 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_IMR3,
Federico Vagabca7ad12012-04-12 12:39:36 -0300577 ADV7180_IRQ3_AD_CHANGE);
578 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300579 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300580
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300581 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_IMR4, 0);
Federico Vagabca7ad12012-04-12 12:39:36 -0300582 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300583 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300584
Lars-Peter Clausen029d6172015-01-23 12:52:23 -0300585 ret = i2c_smbus_write_byte_data(client, ADV7180_REG_CTRL,
Federico Vagabca7ad12012-04-12 12:39:36 -0300586 0);
587 if (ret < 0)
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300588 goto err;
Federico Vagabca7ad12012-04-12 12:39:36 -0300589 }
590
Federico Vagabca7ad12012-04-12 12:39:36 -0300591 return 0;
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300592
593err:
Alexey Khoroshilovdf065b32014-03-14 18:04:03 -0300594 return ret;
Federico Vagabca7ad12012-04-12 12:39:36 -0300595}
Richard Röjfors6789cb52009-09-18 21:17:20 -0300596
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800597static int adv7180_probe(struct i2c_client *client,
598 const struct i2c_device_id *id)
Richard Röjfors6789cb52009-09-18 21:17:20 -0300599{
600 struct adv7180_state *state;
601 struct v4l2_subdev *sd;
602 int ret;
603
604 /* Check if the adapter supports the needed features */
605 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
606 return -EIO;
607
608 v4l_info(client, "chip found @ 0x%02x (%s)\n",
Federico Vagabca7ad12012-04-12 12:39:36 -0300609 client->addr, client->adapter->name);
Richard Röjfors6789cb52009-09-18 21:17:20 -0300610
Laurent Pinchartc02b2112013-05-02 08:29:43 -0300611 state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
Fabio Estevam7657e062014-12-16 13:49:07 -0300612 if (state == NULL)
613 return -ENOMEM;
Richard Röjfors42752f72009-09-22 06:07:06 -0300614
615 state->irq = client->irq;
Richard Röjfors42752f72009-09-22 06:07:06 -0300616 mutex_init(&state->mutex);
Richard Röjforsc277b602009-09-22 06:06:34 -0300617 state->autodetect = true;
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300618 state->powered = true;
Federico Vagabca7ad12012-04-12 12:39:36 -0300619 state->input = 0;
Richard Röjfors6789cb52009-09-18 21:17:20 -0300620 sd = &state->sd;
621 v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
622
Federico Vagac9fbedd2012-07-11 11:29:33 -0300623 ret = adv7180_init_controls(state);
624 if (ret)
Richard Röjfors42752f72009-09-22 06:07:06 -0300625 goto err_unreg_subdev;
Federico Vagac9fbedd2012-07-11 11:29:33 -0300626 ret = init_device(client, state);
627 if (ret)
628 goto err_free_ctrl;
Lars-Peter Clausenfa5b7942014-03-07 13:14:32 -0300629
Lars-Peter Clausenfa5721d2015-01-23 12:52:20 -0300630 if (state->irq) {
631 ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
Lars-Peter Clausenf3e991d2015-01-23 12:52:21 -0300632 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
633 KBUILD_MODNAME, state);
Lars-Peter Clausenfa5721d2015-01-23 12:52:20 -0300634 if (ret)
635 goto err_free_ctrl;
636 }
637
Lars-Peter Clausenfa5b7942014-03-07 13:14:32 -0300638 ret = v4l2_async_register_subdev(sd);
639 if (ret)
640 goto err_free_irq;
641
Richard Röjfors6789cb52009-09-18 21:17:20 -0300642 return 0;
Richard Röjfors42752f72009-09-22 06:07:06 -0300643
Lars-Peter Clausenfa5b7942014-03-07 13:14:32 -0300644err_free_irq:
645 if (state->irq > 0)
646 free_irq(client->irq, state);
Federico Vagac9fbedd2012-07-11 11:29:33 -0300647err_free_ctrl:
648 adv7180_exit_controls(state);
Richard Röjfors42752f72009-09-22 06:07:06 -0300649err_unreg_subdev:
Lars-Peter Clausen297a0ae2014-03-07 13:14:27 -0300650 mutex_destroy(&state->mutex);
Richard Röjfors42752f72009-09-22 06:07:06 -0300651 return ret;
Richard Röjfors6789cb52009-09-18 21:17:20 -0300652}
653
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800654static int adv7180_remove(struct i2c_client *client)
Richard Röjfors6789cb52009-09-18 21:17:20 -0300655{
656 struct v4l2_subdev *sd = i2c_get_clientdata(client);
Richard Röjfors42752f72009-09-22 06:07:06 -0300657 struct adv7180_state *state = to_state(sd);
Richard Röjfors6789cb52009-09-18 21:17:20 -0300658
Lars-Peter Clausenfa5b7942014-03-07 13:14:32 -0300659 v4l2_async_unregister_subdev(sd);
660
Lars-Peter Clausen0c255342014-03-07 13:14:31 -0300661 if (state->irq > 0)
Richard Röjfors42752f72009-09-22 06:07:06 -0300662 free_irq(client->irq, state);
Richard Röjfors42752f72009-09-22 06:07:06 -0300663
Lars-Peter Clausenb13f4af2014-03-07 13:14:28 -0300664 adv7180_exit_controls(state);
Lars-Peter Clausen297a0ae2014-03-07 13:14:27 -0300665 mutex_destroy(&state->mutex);
Richard Röjfors6789cb52009-09-18 21:17:20 -0300666 return 0;
667}
668
669static const struct i2c_device_id adv7180_id[] = {
Federico Vagac9fbedd2012-07-11 11:29:33 -0300670 {KBUILD_MODNAME, 0},
Richard Röjfors6789cb52009-09-18 21:17:20 -0300671 {},
672};
673
Lars-Peter Clausencc1088d2013-04-13 05:25:59 -0300674#ifdef CONFIG_PM_SLEEP
675static int adv7180_suspend(struct device *dev)
Federico Vagabca7ad12012-04-12 12:39:36 -0300676{
Lars-Peter Clausencc1088d2013-04-13 05:25:59 -0300677 struct i2c_client *client = to_i2c_client(dev);
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300678 struct v4l2_subdev *sd = i2c_get_clientdata(client);
679 struct adv7180_state *state = to_state(sd);
Federico Vagabca7ad12012-04-12 12:39:36 -0300680
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300681 return adv7180_set_power(state, client, false);
Federico Vagabca7ad12012-04-12 12:39:36 -0300682}
683
Lars-Peter Clausencc1088d2013-04-13 05:25:59 -0300684static int adv7180_resume(struct device *dev)
Federico Vagabca7ad12012-04-12 12:39:36 -0300685{
Lars-Peter Clausencc1088d2013-04-13 05:25:59 -0300686 struct i2c_client *client = to_i2c_client(dev);
Federico Vagabca7ad12012-04-12 12:39:36 -0300687 struct v4l2_subdev *sd = i2c_get_clientdata(client);
688 struct adv7180_state *state = to_state(sd);
689 int ret;
690
Lars-Peter Clausene246c332014-03-10 14:05:39 -0300691 if (state->powered) {
692 ret = adv7180_set_power(state, client, true);
693 if (ret)
694 return ret;
695 }
Federico Vagabca7ad12012-04-12 12:39:36 -0300696 ret = init_device(client, state);
697 if (ret < 0)
698 return ret;
699 return 0;
700}
Lars-Peter Clausencc1088d2013-04-13 05:25:59 -0300701
702static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
703#define ADV7180_PM_OPS (&adv7180_pm_ops)
704
705#else
706#define ADV7180_PM_OPS NULL
Federico Vagabca7ad12012-04-12 12:39:36 -0300707#endif
708
Richard Röjfors6789cb52009-09-18 21:17:20 -0300709MODULE_DEVICE_TABLE(i2c, adv7180_id);
710
711static struct i2c_driver adv7180_driver = {
712 .driver = {
Federico Vagabca7ad12012-04-12 12:39:36 -0300713 .owner = THIS_MODULE,
Federico Vagac9fbedd2012-07-11 11:29:33 -0300714 .name = KBUILD_MODNAME,
Lars-Peter Clausencc1088d2013-04-13 05:25:59 -0300715 .pm = ADV7180_PM_OPS,
Federico Vagabca7ad12012-04-12 12:39:36 -0300716 },
717 .probe = adv7180_probe,
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800718 .remove = adv7180_remove,
Federico Vagabca7ad12012-04-12 12:39:36 -0300719 .id_table = adv7180_id,
Richard Röjfors6789cb52009-09-18 21:17:20 -0300720};
721
Axel Linc6e8d862012-02-12 06:56:32 -0300722module_i2c_driver(adv7180_driver);
Richard Röjfors6789cb52009-09-18 21:17:20 -0300723
724MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
725MODULE_AUTHOR("Mocean Laboratories");
726MODULE_LICENSE("GPL v2");