blob: 7f7c933b5cf4f55826574f4c73d05712f7c3d3f4 [file] [log] [blame]
Thomas Gleixnerd2912cb2019-06-04 10:11:33 +02001// SPDX-License-Identifier: GPL-2.0-only
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -03002/*
Janusz Krzysztofik23a52382017-06-16 16:45:33 -03003 * V4L2 subdevice driver for OmniVision OV6650 Camera Sensor
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -03004 *
5 * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
6 *
7 * Based on OmniVision OV96xx Camera Driver
8 * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
9 *
10 * Based on ov772x camera driver:
11 * Copyright (C) 2008 Renesas Solutions Corp.
12 * Kuninori Morimoto <morimoto.kuninori@renesas.com>
13 *
14 * Based on ov7670 and soc_camera_platform driver,
15 * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
16 * Copyright (C) 2008 Magnus Damm
17 * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
18 *
Mauro Carvalho Chehabf8a76472019-02-18 14:28:58 -050019 * Hardware specific bits initially based on former work by Matt Callow
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -030020 * drivers/media/video/omap/sensor_ov6650.c
21 * Copyright (C) 2006 Matt Callow
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -030022 */
23
24#include <linux/bitops.h>
25#include <linux/delay.h>
26#include <linux/i2c.h>
27#include <linux/slab.h>
Guennadi Liakhovetski95d20102011-09-09 13:56:04 -030028#include <linux/v4l2-mediabus.h>
Paul Gortmaker7a707b82011-07-03 14:03:12 -040029#include <linux/module.h>
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -030030
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -030031#include <media/v4l2-clk.h>
Hans Verkuilafd96902011-09-12 09:52:01 -030032#include <media/v4l2-ctrls.h>
Janusz Krzysztofik23a52382017-06-16 16:45:33 -030033#include <media/v4l2-device.h>
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -030034
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -030035/* Register definitions */
36#define REG_GAIN 0x00 /* range 00 - 3F */
37#define REG_BLUE 0x01
38#define REG_RED 0x02
39#define REG_SAT 0x03 /* [7:4] saturation [0:3] reserved */
40#define REG_HUE 0x04 /* [7:6] rsrvd [5] hue en [4:0] hue */
41
42#define REG_BRT 0x06
43
44#define REG_PIDH 0x0a
45#define REG_PIDL 0x0b
46
47#define REG_AECH 0x10
48#define REG_CLKRC 0x11 /* Data Format and Internal Clock */
49 /* [7:6] Input system clock (MHz)*/
50 /* 00=8, 01=12, 10=16, 11=24 */
51 /* [5:0]: Internal Clock Pre-Scaler */
52#define REG_COMA 0x12 /* [7] Reset */
53#define REG_COMB 0x13
54#define REG_COMC 0x14
55#define REG_COMD 0x15
56#define REG_COML 0x16
57#define REG_HSTRT 0x17
58#define REG_HSTOP 0x18
59#define REG_VSTRT 0x19
60#define REG_VSTOP 0x1a
61#define REG_PSHFT 0x1b
62#define REG_MIDH 0x1c
63#define REG_MIDL 0x1d
64#define REG_HSYNS 0x1e
65#define REG_HSYNE 0x1f
66#define REG_COME 0x20
67#define REG_YOFF 0x21
68#define REG_UOFF 0x22
69#define REG_VOFF 0x23
70#define REG_AEW 0x24
71#define REG_AEB 0x25
72#define REG_COMF 0x26
73#define REG_COMG 0x27
74#define REG_COMH 0x28
75#define REG_COMI 0x29
76
77#define REG_FRARL 0x2b
78#define REG_COMJ 0x2c
79#define REG_COMK 0x2d
80#define REG_AVGY 0x2e
81#define REG_REF0 0x2f
82#define REG_REF1 0x30
83#define REG_REF2 0x31
84#define REG_FRAJH 0x32
85#define REG_FRAJL 0x33
86#define REG_FACT 0x34
87#define REG_L1AEC 0x35
88#define REG_AVGU 0x36
89#define REG_AVGV 0x37
90
91#define REG_SPCB 0x60
92#define REG_SPCC 0x61
93#define REG_GAM1 0x62
94#define REG_GAM2 0x63
95#define REG_GAM3 0x64
96#define REG_SPCD 0x65
97
98#define REG_SPCE 0x68
99#define REG_ADCL 0x69
100
101#define REG_RMCO 0x6c
102#define REG_GMCO 0x6d
103#define REG_BMCO 0x6e
104
105
106/* Register bits, values, etc. */
107#define OV6650_PIDH 0x66 /* high byte of product ID number */
108#define OV6650_PIDL 0x50 /* low byte of product ID number */
109#define OV6650_MIDH 0x7F /* high byte of mfg ID */
110#define OV6650_MIDL 0xA2 /* low byte of mfg ID */
111
112#define DEF_GAIN 0x00
113#define DEF_BLUE 0x80
114#define DEF_RED 0x80
115
116#define SAT_SHIFT 4
117#define SAT_MASK (0xf << SAT_SHIFT)
118#define SET_SAT(x) (((x) << SAT_SHIFT) & SAT_MASK)
119
120#define HUE_EN BIT(5)
121#define HUE_MASK 0x1f
122#define DEF_HUE 0x10
123#define SET_HUE(x) (HUE_EN | ((x) & HUE_MASK))
124
125#define DEF_AECH 0x4D
126
127#define CLKRC_6MHz 0x00
128#define CLKRC_12MHz 0x40
129#define CLKRC_16MHz 0x80
130#define CLKRC_24MHz 0xc0
131#define CLKRC_DIV_MASK 0x3f
132#define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1)
133
134#define COMA_RESET BIT(7)
135#define COMA_QCIF BIT(5)
136#define COMA_RAW_RGB BIT(4)
137#define COMA_RGB BIT(3)
138#define COMA_BW BIT(2)
139#define COMA_WORD_SWAP BIT(1)
140#define COMA_BYTE_SWAP BIT(0)
141#define DEF_COMA 0x00
142
143#define COMB_FLIP_V BIT(7)
144#define COMB_FLIP_H BIT(5)
145#define COMB_BAND_FILTER BIT(4)
146#define COMB_AWB BIT(2)
147#define COMB_AGC BIT(1)
148#define COMB_AEC BIT(0)
149#define DEF_COMB 0x5f
150
151#define COML_ONE_CHANNEL BIT(7)
152
153#define DEF_HSTRT 0x24
154#define DEF_HSTOP 0xd4
155#define DEF_VSTRT 0x04
156#define DEF_VSTOP 0x94
157
158#define COMF_HREF_LOW BIT(4)
159
160#define COMJ_PCLK_RISING BIT(4)
161#define COMJ_VSYNC_HIGH BIT(0)
162
163/* supported resolutions */
164#define W_QCIF (DEF_HSTOP - DEF_HSTRT)
165#define W_CIF (W_QCIF << 1)
166#define H_QCIF (DEF_VSTOP - DEF_VSTRT)
167#define H_CIF (H_QCIF << 1)
168
169#define FRAME_RATE_MAX 30
170
171
172struct ov6650_reg {
173 u8 reg;
174 u8 val;
175};
176
177struct ov6650 {
178 struct v4l2_subdev subdev;
Hans Verkuilafd96902011-09-12 09:52:01 -0300179 struct v4l2_ctrl_handler hdl;
180 struct {
181 /* exposure/autoexposure cluster */
182 struct v4l2_ctrl *autoexposure;
183 struct v4l2_ctrl *exposure;
184 };
185 struct {
186 /* gain/autogain cluster */
187 struct v4l2_ctrl *autogain;
188 struct v4l2_ctrl *gain;
189 };
190 struct {
191 /* blue/red/autowhitebalance cluster */
192 struct v4l2_ctrl *autowb;
193 struct v4l2_ctrl *blue;
194 struct v4l2_ctrl *red;
195 };
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -0300196 struct v4l2_clk *clk;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300197 bool half_scale; /* scale down output by 2 */
198 struct v4l2_rect rect; /* sensor cropping window */
199 unsigned long pclk_limit; /* from host */
200 unsigned long pclk_max; /* from resolution and format */
Hans Verkuil44711092018-01-22 04:00:45 -0500201 struct v4l2_fract tpf; /* as requested with s_frame_interval */
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300202 u32 code;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300203 enum v4l2_colorspace colorspace;
204};
205
206
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300207static u32 ov6650_codes[] = {
208 MEDIA_BUS_FMT_YUYV8_2X8,
209 MEDIA_BUS_FMT_UYVY8_2X8,
210 MEDIA_BUS_FMT_YVYU8_2X8,
211 MEDIA_BUS_FMT_VYUY8_2X8,
212 MEDIA_BUS_FMT_SBGGR8_1X8,
213 MEDIA_BUS_FMT_Y8_1X8,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300214};
215
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300216/* read a register */
217static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
218{
219 int ret;
220 u8 data = reg;
221 struct i2c_msg msg = {
222 .addr = client->addr,
223 .flags = 0,
224 .len = 1,
225 .buf = &data,
226 };
227
228 ret = i2c_transfer(client->adapter, &msg, 1);
229 if (ret < 0)
230 goto err;
231
232 msg.flags = I2C_M_RD;
233 ret = i2c_transfer(client->adapter, &msg, 1);
234 if (ret < 0)
235 goto err;
236
237 *val = data;
238 return 0;
239
240err:
241 dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
242 return ret;
243}
244
245/* write a register */
246static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
247{
248 int ret;
249 unsigned char data[2] = { reg, val };
250 struct i2c_msg msg = {
251 .addr = client->addr,
252 .flags = 0,
253 .len = 2,
254 .buf = data,
255 };
256
257 ret = i2c_transfer(client->adapter, &msg, 1);
258 udelay(100);
259
260 if (ret < 0) {
261 dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
262 return ret;
263 }
264 return 0;
265}
266
267
268/* Read a register, alter its bits, write it back */
269static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask)
270{
271 u8 val;
272 int ret;
273
274 ret = ov6650_reg_read(client, reg, &val);
275 if (ret) {
276 dev_err(&client->dev,
277 "[Read]-Modify-Write of register 0x%02x failed!\n",
278 reg);
279 return ret;
280 }
281
282 val &= ~mask;
283 val |= set;
284
285 ret = ov6650_reg_write(client, reg, val);
286 if (ret)
287 dev_err(&client->dev,
288 "Read-Modify-[Write] of register 0x%02x failed!\n",
289 reg);
290
291 return ret;
292}
293
294static struct ov6650 *to_ov6650(const struct i2c_client *client)
295{
296 return container_of(i2c_get_clientdata(client), struct ov6650, subdev);
297}
298
299/* Start/Stop streaming from the device */
300static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
301{
302 return 0;
303}
304
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300305/* Get status of additional camera capabilities */
Hans Verkuilafd96902011-09-12 09:52:01 -0300306static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300307{
Hans Verkuilafd96902011-09-12 09:52:01 -0300308 struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
309 struct v4l2_subdev *sd = &priv->subdev;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300310 struct i2c_client *client = v4l2_get_subdevdata(sd);
Hans Verkuilafd96902011-09-12 09:52:01 -0300311 uint8_t reg, reg2;
Janusz Krzysztofik2e56d932011-09-12 08:25:25 -0300312 int ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300313
314 switch (ctrl->id) {
315 case V4L2_CID_AUTOGAIN:
Hans Verkuilafd96902011-09-12 09:52:01 -0300316 ret = ov6650_reg_read(client, REG_GAIN, &reg);
317 if (!ret)
318 priv->gain->val = reg;
319 return ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300320 case V4L2_CID_AUTO_WHITE_BALANCE:
Hans Verkuilafd96902011-09-12 09:52:01 -0300321 ret = ov6650_reg_read(client, REG_BLUE, &reg);
322 if (!ret)
323 ret = ov6650_reg_read(client, REG_RED, &reg2);
324 if (!ret) {
325 priv->blue->val = reg;
326 priv->red->val = reg2;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300327 }
Hans Verkuilafd96902011-09-12 09:52:01 -0300328 return ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300329 case V4L2_CID_EXPOSURE_AUTO:
Hans Verkuilafd96902011-09-12 09:52:01 -0300330 ret = ov6650_reg_read(client, REG_AECH, &reg);
331 if (!ret)
332 priv->exposure->val = reg;
333 return ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300334 }
Hans Verkuilafd96902011-09-12 09:52:01 -0300335 return -EINVAL;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300336}
337
338/* Set status of additional camera capabilities */
Hans Verkuilafd96902011-09-12 09:52:01 -0300339static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300340{
Hans Verkuilafd96902011-09-12 09:52:01 -0300341 struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
342 struct v4l2_subdev *sd = &priv->subdev;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300343 struct i2c_client *client = v4l2_get_subdevdata(sd);
Janusz Krzysztofik2e56d932011-09-12 08:25:25 -0300344 int ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300345
346 switch (ctrl->id) {
347 case V4L2_CID_AUTOGAIN:
348 ret = ov6650_reg_rmw(client, REG_COMB,
Hans Verkuilafd96902011-09-12 09:52:01 -0300349 ctrl->val ? COMB_AGC : 0, COMB_AGC);
350 if (!ret && !ctrl->val)
351 ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
352 return ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300353 case V4L2_CID_AUTO_WHITE_BALANCE:
354 ret = ov6650_reg_rmw(client, REG_COMB,
Hans Verkuilafd96902011-09-12 09:52:01 -0300355 ctrl->val ? COMB_AWB : 0, COMB_AWB);
356 if (!ret && !ctrl->val) {
357 ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
358 if (!ret)
359 ret = ov6650_reg_write(client, REG_RED,
360 priv->red->val);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300361 }
Hans Verkuilafd96902011-09-12 09:52:01 -0300362 return ret;
363 case V4L2_CID_SATURATION:
364 return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
365 SAT_MASK);
366 case V4L2_CID_HUE:
367 return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
368 HUE_MASK);
369 case V4L2_CID_BRIGHTNESS:
370 return ov6650_reg_write(client, REG_BRT, ctrl->val);
371 case V4L2_CID_EXPOSURE_AUTO:
Janusz Krzysztofik2e56d932011-09-12 08:25:25 -0300372 ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
373 V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
Hans Verkuilafd96902011-09-12 09:52:01 -0300374 if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
375 ret = ov6650_reg_write(client, REG_AECH,
376 priv->exposure->val);
377 return ret;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300378 case V4L2_CID_GAMMA:
Hans Verkuilafd96902011-09-12 09:52:01 -0300379 return ov6650_reg_write(client, REG_GAM1, ctrl->val);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300380 case V4L2_CID_VFLIP:
Hans Verkuilafd96902011-09-12 09:52:01 -0300381 return ov6650_reg_rmw(client, REG_COMB,
382 ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300383 case V4L2_CID_HFLIP:
Hans Verkuilafd96902011-09-12 09:52:01 -0300384 return ov6650_reg_rmw(client, REG_COMB,
385 ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300386 }
387
Hans Verkuilafd96902011-09-12 09:52:01 -0300388 return -EINVAL;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300389}
390
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300391#ifdef CONFIG_VIDEO_ADV_DEBUG
392static int ov6650_get_register(struct v4l2_subdev *sd,
393 struct v4l2_dbg_register *reg)
394{
395 struct i2c_client *client = v4l2_get_subdevdata(sd);
396 int ret;
397 u8 val;
398
399 if (reg->reg & ~0xff)
400 return -EINVAL;
401
402 reg->size = 1;
403
404 ret = ov6650_reg_read(client, reg->reg, &val);
405 if (!ret)
406 reg->val = (__u64)val;
407
408 return ret;
409}
410
411static int ov6650_set_register(struct v4l2_subdev *sd,
Hans Verkuil977ba3b12013-03-24 08:28:46 -0300412 const struct v4l2_dbg_register *reg)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300413{
414 struct i2c_client *client = v4l2_get_subdevdata(sd);
415
416 if (reg->reg & ~0xff || reg->val & ~0xff)
417 return -EINVAL;
418
419 return ov6650_reg_write(client, reg->reg, reg->val);
420}
421#endif
422
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300423static int ov6650_s_power(struct v4l2_subdev *sd, int on)
424{
425 struct i2c_client *client = v4l2_get_subdevdata(sd);
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -0300426 struct ov6650 *priv = to_ov6650(client);
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300427 int ret = 0;
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300428
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300429 if (on)
430 ret = v4l2_clk_enable(priv->clk);
431 else
432 v4l2_clk_disable(priv->clk);
433
434 return ret;
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300435}
436
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200437static int ov6650_get_selection(struct v4l2_subdev *sd,
438 struct v4l2_subdev_pad_config *cfg,
439 struct v4l2_subdev_selection *sel)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300440{
441 struct i2c_client *client = v4l2_get_subdevdata(sd);
442 struct ov6650 *priv = to_ov6650(client);
443
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200444 if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
445 return -EINVAL;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300446
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200447 switch (sel->target) {
448 case V4L2_SEL_TGT_CROP_BOUNDS:
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200449 sel->r.left = DEF_HSTRT << 1;
450 sel->r.top = DEF_VSTRT << 1;
451 sel->r.width = W_CIF;
452 sel->r.height = H_CIF;
453 return 0;
454 case V4L2_SEL_TGT_CROP:
455 sel->r = priv->rect;
456 return 0;
457 default:
458 return -EINVAL;
459 }
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300460}
461
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200462static int ov6650_set_selection(struct v4l2_subdev *sd,
463 struct v4l2_subdev_pad_config *cfg,
464 struct v4l2_subdev_selection *sel)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300465{
466 struct i2c_client *client = v4l2_get_subdevdata(sd);
467 struct ov6650 *priv = to_ov6650(client);
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200468 struct v4l2_rect rect = sel->r;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300469 int ret;
470
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200471 if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
472 sel->target != V4L2_SEL_TGT_CROP)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300473 return -EINVAL;
474
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300475 v4l_bound_align_image(&rect.width, 2, W_CIF, 1,
476 &rect.height, 2, H_CIF, 1, 0);
477 v4l_bound_align_image(&rect.left, DEF_HSTRT << 1,
478 (DEF_HSTRT << 1) + W_CIF - (__s32)rect.width, 1,
479 &rect.top, DEF_VSTRT << 1,
480 (DEF_VSTRT << 1) + H_CIF - (__s32)rect.height, 1,
481 0);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300482
Hans Verkuil4f996592012-09-05 05:10:48 -0300483 ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300484 if (!ret) {
Hans Verkuil4f996592012-09-05 05:10:48 -0300485 priv->rect.left = rect.left;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300486 ret = ov6650_reg_write(client, REG_HSTOP,
Hans Verkuil4f996592012-09-05 05:10:48 -0300487 (rect.left + rect.width) >> 1);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300488 }
489 if (!ret) {
Hans Verkuil4f996592012-09-05 05:10:48 -0300490 priv->rect.width = rect.width;
491 ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300492 }
493 if (!ret) {
Hans Verkuil4f996592012-09-05 05:10:48 -0300494 priv->rect.top = rect.top;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300495 ret = ov6650_reg_write(client, REG_VSTOP,
Hans Verkuil4f996592012-09-05 05:10:48 -0300496 (rect.top + rect.height) >> 1);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300497 }
498 if (!ret)
Hans Verkuil4f996592012-09-05 05:10:48 -0300499 priv->rect.height = rect.height;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300500
501 return ret;
502}
503
Hans Verkuilda298c62015-04-09 04:02:34 -0300504static int ov6650_get_fmt(struct v4l2_subdev *sd,
505 struct v4l2_subdev_pad_config *cfg,
506 struct v4l2_subdev_format *format)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300507{
Hans Verkuilda298c62015-04-09 04:02:34 -0300508 struct v4l2_mbus_framefmt *mf = &format->format;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300509 struct i2c_client *client = v4l2_get_subdevdata(sd);
510 struct ov6650 *priv = to_ov6650(client);
511
Hans Verkuilda298c62015-04-09 04:02:34 -0300512 if (format->pad)
513 return -EINVAL;
514
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300515 mf->width = priv->rect.width >> priv->half_scale;
516 mf->height = priv->rect.height >> priv->half_scale;
517 mf->code = priv->code;
518 mf->colorspace = priv->colorspace;
519 mf->field = V4L2_FIELD_NONE;
520
521 return 0;
522}
523
524static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
525{
Janusz Krzysztofikd889eb12010-11-02 13:14:36 -0300526 return width > rect->width >> 1 || height > rect->height >> 1;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300527}
528
529static u8 to_clkrc(struct v4l2_fract *timeperframe,
530 unsigned long pclk_limit, unsigned long pclk_max)
531{
532 unsigned long pclk;
533
534 if (timeperframe->numerator && timeperframe->denominator)
535 pclk = pclk_max * timeperframe->denominator /
536 (FRAME_RATE_MAX * timeperframe->numerator);
537 else
538 pclk = pclk_max;
539
540 if (pclk_limit && pclk_limit < pclk)
541 pclk = pclk_limit;
542
543 return (pclk_max - 1) / pclk;
544}
545
546/* set the format we will capture in */
547static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
548{
549 struct i2c_client *client = v4l2_get_subdevdata(sd);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300550 struct ov6650 *priv = to_ov6650(client);
551 bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200552 struct v4l2_subdev_selection sel = {
553 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
554 .target = V4L2_SEL_TGT_CROP,
555 .r.left = priv->rect.left + (priv->rect.width >> 1) -
556 (mf->width >> (1 - half_scale)),
557 .r.top = priv->rect.top + (priv->rect.height >> 1) -
558 (mf->height >> (1 - half_scale)),
559 .r.width = mf->width << half_scale,
560 .r.height = mf->height << half_scale,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300561 };
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300562 u32 code = mf->code;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300563 unsigned long mclk, pclk;
564 u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc;
565 int ret;
566
567 /* select color matrix configuration for given color encoding */
568 switch (code) {
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300569 case MEDIA_BUS_FMT_Y8_1X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300570 dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
571 coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
572 coma_set |= COMA_BW;
573 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300574 case MEDIA_BUS_FMT_YUYV8_2X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300575 dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
576 coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
577 coma_set |= COMA_WORD_SWAP;
578 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300579 case MEDIA_BUS_FMT_YVYU8_2X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300580 dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
581 coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
582 COMA_BYTE_SWAP;
583 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300584 case MEDIA_BUS_FMT_UYVY8_2X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300585 dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
586 if (half_scale) {
587 coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
588 coma_set |= COMA_BYTE_SWAP;
589 } else {
590 coma_mask |= COMA_RGB | COMA_BW;
591 coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
592 }
593 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300594 case MEDIA_BUS_FMT_VYUY8_2X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300595 dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
596 if (half_scale) {
597 coma_mask |= COMA_RGB | COMA_BW;
598 coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
599 } else {
600 coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
601 coma_set |= COMA_BYTE_SWAP;
602 }
603 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300604 case MEDIA_BUS_FMT_SBGGR8_1X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300605 dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
606 coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
607 coma_set |= COMA_RAW_RGB | COMA_RGB;
608 break;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300609 default:
610 dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
611 return -EINVAL;
612 }
613 priv->code = code;
614
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300615 if (code == MEDIA_BUS_FMT_Y8_1X8 ||
616 code == MEDIA_BUS_FMT_SBGGR8_1X8) {
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300617 coml_mask = COML_ONE_CHANNEL;
618 coml_set = 0;
619 priv->pclk_max = 4000000;
620 } else {
621 coml_mask = 0;
622 coml_set = COML_ONE_CHANNEL;
623 priv->pclk_max = 8000000;
624 }
625
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300626 if (code == MEDIA_BUS_FMT_SBGGR8_1X8)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300627 priv->colorspace = V4L2_COLORSPACE_SRGB;
628 else if (code != 0)
629 priv->colorspace = V4L2_COLORSPACE_JPEG;
630
631 if (half_scale) {
632 dev_dbg(&client->dev, "max resolution: QCIF\n");
633 coma_set |= COMA_QCIF;
634 priv->pclk_max /= 2;
635 } else {
636 dev_dbg(&client->dev, "max resolution: CIF\n");
637 coma_mask |= COMA_QCIF;
638 }
639 priv->half_scale = half_scale;
640
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300641 clkrc = CLKRC_12MHz;
642 mclk = 12000000;
643 priv->pclk_limit = 1334000;
644 dev_dbg(&client->dev, "using 12MHz input clock\n");
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300645
646 clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
647
648 pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc);
649 dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n",
650 mclk / pclk, 10 * mclk % pclk / pclk);
651
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200652 ret = ov6650_set_selection(sd, NULL, &sel);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300653 if (!ret)
654 ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
655 if (!ret)
656 ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
657 if (!ret)
658 ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
659
660 if (!ret) {
661 mf->colorspace = priv->colorspace;
662 mf->width = priv->rect.width >> half_scale;
663 mf->height = priv->rect.height >> half_scale;
664 }
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300665 return ret;
666}
667
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300668static int ov6650_set_fmt(struct v4l2_subdev *sd,
669 struct v4l2_subdev_pad_config *cfg,
670 struct v4l2_subdev_format *format)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300671{
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300672 struct v4l2_mbus_framefmt *mf = &format->format;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300673 struct i2c_client *client = v4l2_get_subdevdata(sd);
674 struct ov6650 *priv = to_ov6650(client);
675
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300676 if (format->pad)
677 return -EINVAL;
678
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300679 if (is_unscaled_ok(mf->width, mf->height, &priv->rect))
680 v4l_bound_align_image(&mf->width, 2, W_CIF, 1,
681 &mf->height, 2, H_CIF, 1, 0);
682
683 mf->field = V4L2_FIELD_NONE;
684
685 switch (mf->code) {
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300686 case MEDIA_BUS_FMT_Y10_1X10:
687 mf->code = MEDIA_BUS_FMT_Y8_1X8;
Mauro Carvalho Chehab06eeefe2017-05-18 08:13:28 -0300688 /* fall through */
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300689 case MEDIA_BUS_FMT_Y8_1X8:
690 case MEDIA_BUS_FMT_YVYU8_2X8:
691 case MEDIA_BUS_FMT_YUYV8_2X8:
692 case MEDIA_BUS_FMT_VYUY8_2X8:
693 case MEDIA_BUS_FMT_UYVY8_2X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300694 mf->colorspace = V4L2_COLORSPACE_JPEG;
695 break;
696 default:
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300697 mf->code = MEDIA_BUS_FMT_SBGGR8_1X8;
Mauro Carvalho Chehab06eeefe2017-05-18 08:13:28 -0300698 /* fall through */
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300699 case MEDIA_BUS_FMT_SBGGR8_1X8:
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300700 mf->colorspace = V4L2_COLORSPACE_SRGB;
701 break;
702 }
703
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300704 if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
705 return ov6650_s_fmt(sd, mf);
706 cfg->try_fmt = *mf;
707
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300708 return 0;
709}
710
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300711static int ov6650_enum_mbus_code(struct v4l2_subdev *sd,
712 struct v4l2_subdev_pad_config *cfg,
713 struct v4l2_subdev_mbus_code_enum *code)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300714{
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300715 if (code->pad || code->index >= ARRAY_SIZE(ov6650_codes))
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300716 return -EINVAL;
717
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300718 code->code = ov6650_codes[code->index];
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300719 return 0;
720}
721
Hans Verkuil44711092018-01-22 04:00:45 -0500722static int ov6650_g_frame_interval(struct v4l2_subdev *sd,
723 struct v4l2_subdev_frame_interval *ival)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300724{
725 struct i2c_client *client = v4l2_get_subdevdata(sd);
726 struct ov6650 *priv = to_ov6650(client);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300727
Hans Verkuil44711092018-01-22 04:00:45 -0500728 ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300729 priv->pclk_limit, priv->pclk_max));
Hans Verkuil44711092018-01-22 04:00:45 -0500730 ival->interval.denominator = FRAME_RATE_MAX;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300731
732 dev_dbg(&client->dev, "Frame interval: %u/%u s\n",
Hans Verkuil44711092018-01-22 04:00:45 -0500733 ival->interval.numerator, ival->interval.denominator);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300734
735 return 0;
736}
737
Hans Verkuil44711092018-01-22 04:00:45 -0500738static int ov6650_s_frame_interval(struct v4l2_subdev *sd,
739 struct v4l2_subdev_frame_interval *ival)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300740{
741 struct i2c_client *client = v4l2_get_subdevdata(sd);
742 struct ov6650 *priv = to_ov6650(client);
Hans Verkuil44711092018-01-22 04:00:45 -0500743 struct v4l2_fract *tpf = &ival->interval;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300744 int div, ret;
745 u8 clkrc;
746
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300747 if (tpf->numerator == 0 || tpf->denominator == 0)
748 div = 1; /* Reset to full rate */
749 else
750 div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator;
751
752 if (div == 0)
753 div = 1;
754 else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK))
755 div = GET_CLKRC_DIV(CLKRC_DIV_MASK);
756
757 /*
758 * Keep result to be used as tpf limit
Mauro Carvalho Chehabf8a76472019-02-18 14:28:58 -0500759 * for subsequent clock divider calculations
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300760 */
761 priv->tpf.numerator = div;
762 priv->tpf.denominator = FRAME_RATE_MAX;
763
764 clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
765
766 ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK);
767 if (!ret) {
768 tpf->numerator = GET_CLKRC_DIV(clkrc);
769 tpf->denominator = FRAME_RATE_MAX;
770 }
771
772 return ret;
773}
774
775/* Soft reset the camera. This has nothing to do with the RESET pin! */
776static int ov6650_reset(struct i2c_client *client)
777{
778 int ret;
779
780 dev_dbg(&client->dev, "reset\n");
781
782 ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0);
783 if (ret)
784 dev_err(&client->dev,
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300785 "An error occurred while entering soft reset!\n");
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300786
787 return ret;
788}
789
790/* program default register values */
791static int ov6650_prog_dflt(struct i2c_client *client)
792{
793 int ret;
794
795 dev_dbg(&client->dev, "initializing\n");
796
797 ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */
798 if (!ret)
799 ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER);
800
801 return ret;
802}
803
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -0400804static int ov6650_video_probe(struct v4l2_subdev *sd)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300805{
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -0400806 struct i2c_client *client = v4l2_get_subdevdata(sd);
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300807 struct ov6650 *priv = to_ov6650(client);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300808 u8 pidh, pidl, midh, midl;
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300809 int ret;
810
Janusz Krzysztofikccdd85d2019-03-29 21:06:09 -0400811 priv->clk = v4l2_clk_get(&client->dev, NULL);
812 if (IS_ERR(priv->clk)) {
813 ret = PTR_ERR(priv->clk);
814 dev_err(&client->dev, "v4l2_clk request err: %d\n", ret);
815 return ret;
816 }
817
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -0400818 ret = ov6650_s_power(sd, 1);
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300819 if (ret < 0)
Janusz Krzysztofikccdd85d2019-03-29 21:06:09 -0400820 goto eclkput;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300821
Janusz Krzysztofik933c1322019-03-24 20:21:12 -0400822 msleep(20);
823
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300824 /*
825 * check and show product ID and manufacturer ID
826 */
827 ret = ov6650_reg_read(client, REG_PIDH, &pidh);
828 if (!ret)
829 ret = ov6650_reg_read(client, REG_PIDL, &pidl);
830 if (!ret)
831 ret = ov6650_reg_read(client, REG_MIDH, &midh);
832 if (!ret)
833 ret = ov6650_reg_read(client, REG_MIDL, &midl);
834
835 if (ret)
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300836 goto done;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300837
838 if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) {
839 dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n",
840 pidh, pidl);
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300841 ret = -ENODEV;
842 goto done;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300843 }
844
845 dev_info(&client->dev,
846 "ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n",
847 pidh, pidl, midh, midl);
848
849 ret = ov6650_reset(client);
850 if (!ret)
851 ret = ov6650_prog_dflt(client);
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300852 if (!ret)
853 ret = v4l2_ctrl_handler_setup(&priv->hdl);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300854
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300855done:
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -0400856 ov6650_s_power(sd, 0);
Janusz Krzysztofikccdd85d2019-03-29 21:06:09 -0400857 if (!ret)
858 return 0;
859eclkput:
860 v4l2_clk_put(priv->clk);
861
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300862 return ret;
863}
864
Hans Verkuilafd96902011-09-12 09:52:01 -0300865static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
866 .g_volatile_ctrl = ov6550_g_volatile_ctrl,
867 .s_ctrl = ov6550_s_ctrl,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300868};
869
Bhumika Goyal6713c882016-12-12 11:59:42 -0200870static const struct v4l2_subdev_core_ops ov6650_core_ops = {
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300871#ifdef CONFIG_VIDEO_ADV_DEBUG
872 .g_register = ov6650_get_register,
873 .s_register = ov6650_set_register,
874#endif
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300875 .s_power = ov6650_s_power,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300876};
877
Guennadi Liakhovetskidb669e72011-07-28 14:42:25 -0300878/* Request bus settings on camera side */
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300879static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
880 struct v4l2_mbus_config *cfg)
881{
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300882
883 cfg->flags = V4L2_MBUS_MASTER |
884 V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
885 V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
886 V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
887 V4L2_MBUS_DATA_ACTIVE_HIGH;
888 cfg->type = V4L2_MBUS_PARALLEL;
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300889
890 return 0;
891}
892
Guennadi Liakhovetskidb669e72011-07-28 14:42:25 -0300893/* Alter bus settings on camera side */
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300894static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
895 const struct v4l2_mbus_config *cfg)
896{
897 struct i2c_client *client = v4l2_get_subdevdata(sd);
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300898 int ret;
899
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300900 if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300901 ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
902 else
903 ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
904 if (ret)
905 return ret;
906
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300907 if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300908 ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
909 else
910 ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
911 if (ret)
912 return ret;
913
Janusz Krzysztofik23a52382017-06-16 16:45:33 -0300914 if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300915 ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
916 else
917 ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
918
919 return ret;
920}
921
Bhumika Goyal6713c882016-12-12 11:59:42 -0200922static const struct v4l2_subdev_video_ops ov6650_video_ops = {
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300923 .s_stream = ov6650_s_stream,
Hans Verkuil44711092018-01-22 04:00:45 -0500924 .g_frame_interval = ov6650_g_frame_interval,
925 .s_frame_interval = ov6650_s_frame_interval,
Guennadi Liakhovetski59ca25b2011-07-26 12:02:00 -0300926 .g_mbus_config = ov6650_g_mbus_config,
927 .s_mbus_config = ov6650_s_mbus_config,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300928};
929
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300930static const struct v4l2_subdev_pad_ops ov6650_pad_ops = {
931 .enum_mbus_code = ov6650_enum_mbus_code,
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200932 .get_selection = ov6650_get_selection,
933 .set_selection = ov6650_set_selection,
Hans Verkuilda298c62015-04-09 04:02:34 -0300934 .get_fmt = ov6650_get_fmt,
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300935 .set_fmt = ov6650_set_fmt,
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300936};
937
Bhumika Goyal6713c882016-12-12 11:59:42 -0200938static const struct v4l2_subdev_ops ov6650_subdev_ops = {
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300939 .core = &ov6650_core_ops,
940 .video = &ov6650_video_ops,
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300941 .pad = &ov6650_pad_ops,
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300942};
943
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -0400944static const struct v4l2_subdev_internal_ops ov6650_internal_ops = {
945 .registered = ov6650_video_probe,
946};
947
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300948/*
949 * i2c_driver function
950 */
951static int ov6650_probe(struct i2c_client *client,
952 const struct i2c_device_id *did)
953{
954 struct ov6650 *priv;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300955 int ret;
956
Guennadi Liakhovetski70e176a2012-12-21 10:28:43 -0300957 priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
Markus Elfring0fd58432017-09-02 11:07:31 -0300958 if (!priv)
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300959 return -ENOMEM;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300960
961 v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
Hans Verkuilafd96902011-09-12 09:52:01 -0300962 v4l2_ctrl_handler_init(&priv->hdl, 13);
963 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
964 V4L2_CID_VFLIP, 0, 1, 1, 0);
965 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
966 V4L2_CID_HFLIP, 0, 1, 1, 0);
967 priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
968 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
969 priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
970 V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
971 priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
972 V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
973 priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
974 V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
975 priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
976 V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
977 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
978 V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
979 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
980 V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
981 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
982 V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
983 priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
Janusz Krzysztofik2e56d932011-09-12 08:25:25 -0300984 &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
985 V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
Hans Verkuilafd96902011-09-12 09:52:01 -0300986 priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
987 V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
988 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
989 V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300990
Hans Verkuilafd96902011-09-12 09:52:01 -0300991 priv->subdev.ctrl_handler = &priv->hdl;
Guennadi Liakhovetski70e176a2012-12-21 10:28:43 -0300992 if (priv->hdl.error)
993 return priv->hdl.error;
Hans Verkuilafd96902011-09-12 09:52:01 -0300994
Hans Verkuilafd96902011-09-12 09:52:01 -0300995 v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
996 v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
997 v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
998 V4L2_EXPOSURE_MANUAL, true);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -0300999
1000 priv->rect.left = DEF_HSTRT << 1;
1001 priv->rect.top = DEF_VSTRT << 1;
1002 priv->rect.width = W_CIF;
1003 priv->rect.height = H_CIF;
1004 priv->half_scale = false;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -03001005 priv->code = MEDIA_BUS_FMT_YUYV8_2X8;
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -03001006 priv->colorspace = V4L2_COLORSPACE_JPEG;
1007
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -04001008 priv->subdev.internal_ops = &ov6650_internal_ops;
1009 priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1010
1011 ret = v4l2_async_register_subdev(&priv->subdev);
Janusz Krzysztofikccdd85d2019-03-29 21:06:09 -04001012 if (ret)
Hans Verkuilafd96902011-09-12 09:52:01 -03001013 v4l2_ctrl_handler_free(&priv->hdl);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -03001014
1015 return ret;
1016}
1017
1018static int ov6650_remove(struct i2c_client *client)
1019{
1020 struct ov6650 *priv = to_ov6650(client);
1021
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -03001022 v4l2_clk_put(priv->clk);
Janusz Krzysztofikc62b9602019-03-29 21:06:10 -04001023 v4l2_async_unregister_subdev(&priv->subdev);
Hans Verkuilafd96902011-09-12 09:52:01 -03001024 v4l2_ctrl_handler_free(&priv->hdl);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -03001025 return 0;
1026}
1027
1028static const struct i2c_device_id ov6650_id[] = {
1029 { "ov6650", 0 },
1030 { }
1031};
1032MODULE_DEVICE_TABLE(i2c, ov6650_id);
1033
1034static struct i2c_driver ov6650_i2c_driver = {
1035 .driver = {
1036 .name = "ov6650",
1037 },
1038 .probe = ov6650_probe,
1039 .remove = ov6650_remove,
1040 .id_table = ov6650_id,
1041};
1042
Axel Linc6e8d862012-02-12 06:56:32 -03001043module_i2c_driver(ov6650_i2c_driver);
Janusz Krzysztofik2f6e2402010-10-05 11:52:45 -03001044
1045MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
1046MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
1047MODULE_LICENSE("GPL v2");