aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video
diff options
context:
space:
mode:
authorAndrew Chew <achew@nvidia.com>2011-02-17 17:14:33 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-03-21 19:32:16 -0400
commit9bd060e492a2725c55404988ceed473a090bd04d (patch)
treee21b632e608111d5be28e982150af1ca5bee7dde /drivers/media/video
parent787d0f9d5ca3e98f23af94037094a9a3fafec665 (diff)
[media] V4L: Initial submit of OV9740 driver
This soc_camera driver is for Omnivision's OV9740 sensor. This initial submission provides support for YUV422 output at 1280x720 (720p), which is the sensor's native resolution. 640x480 (VGA) is also supported, with cropping and scaling performed by the sensor's ISP. This driver is heavily based off of the existing OV9640 driver. Signed-off-by: Andrew Chew <achew@nvidia.com> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig6
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/ov9740.c1009
3 files changed, 1016 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index cf81bd536a71..e2f5a69aa400 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -838,6 +838,12 @@ config SOC_CAMERA_OV9640
838 help 838 help
839 This is a ov9640 camera driver 839 This is a ov9640 camera driver
840 840
841config SOC_CAMERA_OV9740
842 tristate "ov9740 camera support"
843 depends on SOC_CAMERA && I2C
844 help
845 This is a ov9740 camera driver
846
841config MX1_VIDEO 847config MX1_VIDEO
842 bool 848 bool
843 849
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 251b7cac3f91..ac54652396e3 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
79obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o 79obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
80obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o 80obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
81obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o 81obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
82obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o
82obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o 83obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o
83obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o 84obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
84 85
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
new file mode 100644
index 000000000000..4d4ee4faca69
--- /dev/null
+++ b/drivers/media/video/ov9740.c
@@ -0,0 +1,1009 @@
1/*
2 * OmniVision OV9740 Camera Driver
3 *
4 * Copyright (C) 2011 NVIDIA Corporation
5 *
6 * Based on ov9640 camera driver.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/i2c.h>
16#include <linux/slab.h>
17#include <media/v4l2-chip-ident.h>
18#include <media/soc_camera.h>
19
20#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
21
22/* General Status Registers */
23#define OV9740_MODEL_ID_HI 0x0000
24#define OV9740_MODEL_ID_LO 0x0001
25#define OV9740_REVISION_NUMBER 0x0002
26#define OV9740_MANUFACTURER_ID 0x0003
27#define OV9740_SMIA_VERSION 0x0004
28
29/* General Setup Registers */
30#define OV9740_MODE_SELECT 0x0100
31#define OV9740_IMAGE_ORT 0x0101
32#define OV9740_SOFTWARE_RESET 0x0103
33#define OV9740_GRP_PARAM_HOLD 0x0104
34#define OV9740_MSK_CORRUP_FM 0x0105
35
36/* Timing Setting */
37#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */
38#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */
39#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */
40#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */
41#define OV9740_X_ADDR_START_HI 0x0344
42#define OV9740_X_ADDR_START_LO 0x0345
43#define OV9740_Y_ADDR_START_HI 0x0346
44#define OV9740_Y_ADDR_START_LO 0x0347
45#define OV9740_X_ADDR_END_HI 0x0348
46#define OV9740_X_ADDR_END_LO 0x0349
47#define OV9740_Y_ADDR_END_HI 0x034A
48#define OV9740_Y_ADDR_END_LO 0x034B
49#define OV9740_X_OUTPUT_SIZE_HI 0x034C
50#define OV9740_X_OUTPUT_SIZE_LO 0x034D
51#define OV9740_Y_OUTPUT_SIZE_HI 0x034E
52#define OV9740_Y_OUTPUT_SIZE_LO 0x034F
53
54/* IO Control Registers */
55#define OV9740_IO_CREL00 0x3002
56#define OV9740_IO_CREL01 0x3004
57#define OV9740_IO_CREL02 0x3005
58#define OV9740_IO_OUTPUT_SEL01 0x3026
59#define OV9740_IO_OUTPUT_SEL02 0x3027
60
61/* AWB Registers */
62#define OV9740_AWB_MANUAL_CTRL 0x3406
63
64/* Analog Control Registers */
65#define OV9740_ANALOG_CTRL01 0x3601
66#define OV9740_ANALOG_CTRL02 0x3602
67#define OV9740_ANALOG_CTRL03 0x3603
68#define OV9740_ANALOG_CTRL04 0x3604
69#define OV9740_ANALOG_CTRL10 0x3610
70#define OV9740_ANALOG_CTRL12 0x3612
71#define OV9740_ANALOG_CTRL20 0x3620
72#define OV9740_ANALOG_CTRL21 0x3621
73#define OV9740_ANALOG_CTRL22 0x3622
74#define OV9740_ANALOG_CTRL30 0x3630
75#define OV9740_ANALOG_CTRL31 0x3631
76#define OV9740_ANALOG_CTRL32 0x3632
77#define OV9740_ANALOG_CTRL33 0x3633
78
79/* Sensor Control */
80#define OV9740_SENSOR_CTRL03 0x3703
81#define OV9740_SENSOR_CTRL04 0x3704
82#define OV9740_SENSOR_CTRL05 0x3705
83#define OV9740_SENSOR_CTRL07 0x3707
84
85/* Timing Control */
86#define OV9740_TIMING_CTRL17 0x3817
87#define OV9740_TIMING_CTRL19 0x3819
88#define OV9740_TIMING_CTRL33 0x3833
89#define OV9740_TIMING_CTRL35 0x3835
90
91/* Banding Filter */
92#define OV9740_AEC_MAXEXPO_60_H 0x3A02
93#define OV9740_AEC_MAXEXPO_60_L 0x3A03
94#define OV9740_AEC_B50_STEP_HI 0x3A08
95#define OV9740_AEC_B50_STEP_LO 0x3A09
96#define OV9740_AEC_B60_STEP_HI 0x3A0A
97#define OV9740_AEC_B60_STEP_LO 0x3A0B
98#define OV9740_AEC_CTRL0D 0x3A0D
99#define OV9740_AEC_CTRL0E 0x3A0E
100#define OV9740_AEC_MAXEXPO_50_H 0x3A14
101#define OV9740_AEC_MAXEXPO_50_L 0x3A15
102
103/* AEC/AGC Control */
104#define OV9740_AEC_ENABLE 0x3503
105#define OV9740_GAIN_CEILING_01 0x3A18
106#define OV9740_GAIN_CEILING_02 0x3A19
107#define OV9740_AEC_HI_THRESHOLD 0x3A11
108#define OV9740_AEC_3A1A 0x3A1A
109#define OV9740_AEC_CTRL1B_WPT2 0x3A1B
110#define OV9740_AEC_CTRL0F_WPT 0x3A0F
111#define OV9740_AEC_CTRL10_BPT 0x3A10
112#define OV9740_AEC_CTRL1E_BPT2 0x3A1E
113#define OV9740_AEC_LO_THRESHOLD 0x3A1F
114
115/* BLC Control */
116#define OV9740_BLC_AUTO_ENABLE 0x4002
117#define OV9740_BLC_MODE 0x4005
118
119/* VFIFO */
120#define OV9740_VFIFO_READ_START_HI 0x4608
121#define OV9740_VFIFO_READ_START_LO 0x4609
122
123/* DVP Control */
124#define OV9740_DVP_VSYNC_CTRL02 0x4702
125#define OV9740_DVP_VSYNC_MODE 0x4704
126#define OV9740_DVP_VSYNC_CTRL06 0x4706
127
128/* PLL Setting */
129#define OV9740_PLL_MODE_CTRL01 0x3104
130#define OV9740_PRE_PLL_CLK_DIV 0x0305
131#define OV9740_PLL_MULTIPLIER 0x0307
132#define OV9740_VT_SYS_CLK_DIV 0x0303
133#define OV9740_VT_PIX_CLK_DIV 0x0301
134#define OV9740_PLL_CTRL3010 0x3010
135#define OV9740_VFIFO_CTRL00 0x460E
136
137/* ISP Control */
138#define OV9740_ISP_CTRL00 0x5000
139#define OV9740_ISP_CTRL01 0x5001
140#define OV9740_ISP_CTRL03 0x5003
141#define OV9740_ISP_CTRL05 0x5005
142#define OV9740_ISP_CTRL12 0x5012
143#define OV9740_ISP_CTRL19 0x5019
144#define OV9740_ISP_CTRL1A 0x501A
145#define OV9740_ISP_CTRL1E 0x501E
146#define OV9740_ISP_CTRL1F 0x501F
147#define OV9740_ISP_CTRL20 0x5020
148#define OV9740_ISP_CTRL21 0x5021
149
150/* AWB */
151#define OV9740_AWB_CTRL00 0x5180
152#define OV9740_AWB_CTRL01 0x5181
153#define OV9740_AWB_CTRL02 0x5182
154#define OV9740_AWB_CTRL03 0x5183
155#define OV9740_AWB_ADV_CTRL01 0x5184
156#define OV9740_AWB_ADV_CTRL02 0x5185
157#define OV9740_AWB_ADV_CTRL03 0x5186
158#define OV9740_AWB_ADV_CTRL04 0x5187
159#define OV9740_AWB_ADV_CTRL05 0x5188
160#define OV9740_AWB_ADV_CTRL06 0x5189
161#define OV9740_AWB_ADV_CTRL07 0x518A
162#define OV9740_AWB_ADV_CTRL08 0x518B
163#define OV9740_AWB_ADV_CTRL09 0x518C
164#define OV9740_AWB_ADV_CTRL10 0x518D
165#define OV9740_AWB_ADV_CTRL11 0x518E
166#define OV9740_AWB_CTRL0F 0x518F
167#define OV9740_AWB_CTRL10 0x5190
168#define OV9740_AWB_CTRL11 0x5191
169#define OV9740_AWB_CTRL12 0x5192
170#define OV9740_AWB_CTRL13 0x5193
171#define OV9740_AWB_CTRL14 0x5194
172
173/* MIPI Control */
174#define OV9740_MIPI_CTRL00 0x4800
175#define OV9740_MIPI_3837 0x3837
176#define OV9740_MIPI_CTRL01 0x4801
177#define OV9740_MIPI_CTRL03 0x4803
178#define OV9740_MIPI_CTRL05 0x4805
179#define OV9740_VFIFO_RD_CTRL 0x4601
180#define OV9740_MIPI_CTRL_3012 0x3012
181#define OV9740_SC_CMMM_MIPI_CTR 0x3014
182
183/* supported resolutions */
184enum {
185 OV9740_VGA,
186 OV9740_720P,
187};
188
189struct ov9740_resolution {
190 unsigned int width;
191 unsigned int height;
192};
193
194static struct ov9740_resolution ov9740_resolutions[] = {
195 [OV9740_VGA] = {
196 .width = 640,
197 .height = 480,
198 },
199 [OV9740_720P] = {
200 .width = 1280,
201 .height = 720,
202 },
203};
204
205/* Misc. structures */
206struct ov9740_reg {
207 u16 reg;
208 u8 val;
209};
210
211struct ov9740_priv {
212 struct v4l2_subdev subdev;
213
214 int ident;
215 u16 model;
216 u8 revision;
217 u8 manid;
218 u8 smiaver;
219
220 bool flag_vflip;
221 bool flag_hflip;
222};
223
224static const struct ov9740_reg ov9740_defaults[] = {
225 /* Banding Filter */
226 { OV9740_AEC_B50_STEP_HI, 0x00 },
227 { OV9740_AEC_B50_STEP_LO, 0xe8 },
228 { OV9740_AEC_CTRL0E, 0x03 },
229 { OV9740_AEC_MAXEXPO_50_H, 0x15 },
230 { OV9740_AEC_MAXEXPO_50_L, 0xc6 },
231 { OV9740_AEC_B60_STEP_HI, 0x00 },
232 { OV9740_AEC_B60_STEP_LO, 0xc0 },
233 { OV9740_AEC_CTRL0D, 0x04 },
234 { OV9740_AEC_MAXEXPO_60_H, 0x18 },
235 { OV9740_AEC_MAXEXPO_60_L, 0x20 },
236
237 /* LC */
238 { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 },
239 { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc },
240
241 /* Un-documented OV9740 registers */
242 { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 },
243 { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c },
244 { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580A, 0x0e }, { 0x580B, 0x16 },
245 { 0x580C, 0x06 }, { 0x580D, 0x02 }, { 0x580E, 0x00 }, { 0x580F, 0x00 },
246 { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 },
247 { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 },
248 { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581A, 0x07 }, { 0x581B, 0x08 },
249 { 0x581C, 0x0b }, { 0x581D, 0x14 }, { 0x581E, 0x28 }, { 0x581F, 0x23 },
250 { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a },
251 { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f },
252 { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582A, 0x8f }, { 0x582B, 0x9e },
253 { 0x582C, 0x8f }, { 0x582D, 0x9f }, { 0x582E, 0x4f }, { 0x582F, 0x87 },
254 { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f },
255 { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf },
256 { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583A, 0x9f }, { 0x583B, 0x7f },
257 { 0x583C, 0x5f },
258
259 /* Y Gamma */
260 { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e },
261 { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 },
262 { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548A, 0xa4 }, { 0x548B, 0xb1 },
263 { 0x548C, 0xc6 }, { 0x548D, 0xd8 }, { 0x548E, 0xe9 },
264
265 /* UV Gamma */
266 { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 },
267 { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 },
268 { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549A, 0x02 }, { 0x549B, 0xeb },
269 { 0x549C, 0x02 }, { 0x549D, 0xa0 }, { 0x549E, 0x02 }, { 0x549F, 0x67 },
270 { 0x54A0, 0x02 }, { 0x54A1, 0x3b }, { 0x54A2, 0x02 }, { 0x54A3, 0x18 },
271 { 0x54A4, 0x01 }, { 0x54A5, 0xe7 }, { 0x54A6, 0x01 }, { 0x54A7, 0xc3 },
272 { 0x54A8, 0x01 }, { 0x54A9, 0x94 }, { 0x54AA, 0x01 }, { 0x54AB, 0x72 },
273 { 0x54AC, 0x01 }, { 0x54AD, 0x57 },
274
275 /* AWB */
276 { OV9740_AWB_CTRL00, 0xf0 },
277 { OV9740_AWB_CTRL01, 0x00 },
278 { OV9740_AWB_CTRL02, 0x41 },
279 { OV9740_AWB_CTRL03, 0x42 },
280 { OV9740_AWB_ADV_CTRL01, 0x8a },
281 { OV9740_AWB_ADV_CTRL02, 0x61 },
282 { OV9740_AWB_ADV_CTRL03, 0xce },
283 { OV9740_AWB_ADV_CTRL04, 0xa8 },
284 { OV9740_AWB_ADV_CTRL05, 0x17 },
285 { OV9740_AWB_ADV_CTRL06, 0x1f },
286 { OV9740_AWB_ADV_CTRL07, 0x27 },
287 { OV9740_AWB_ADV_CTRL08, 0x41 },
288 { OV9740_AWB_ADV_CTRL09, 0x34 },
289 { OV9740_AWB_ADV_CTRL10, 0xf0 },
290 { OV9740_AWB_ADV_CTRL11, 0x10 },
291 { OV9740_AWB_CTRL0F, 0xff },
292 { OV9740_AWB_CTRL10, 0x00 },
293 { OV9740_AWB_CTRL11, 0xff },
294 { OV9740_AWB_CTRL12, 0x00 },
295 { OV9740_AWB_CTRL13, 0xff },
296 { OV9740_AWB_CTRL14, 0x00 },
297
298 /* CIP */
299 { 0x530D, 0x12 },
300
301 /* CMX */
302 { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 },
303 { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 },
304 { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538A, 0x00 }, { 0x538B, 0x20 },
305 { 0x538C, 0x00 }, { 0x538D, 0x00 }, { 0x538E, 0x00 }, { 0x538F, 0x16 },
306 { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 },
307 { 0x5394, 0x18 },
308
309 /* 50/60 Detection */
310 { 0x3C0A, 0x9c }, { 0x3C0B, 0x3f },
311
312 /* Output Select */
313 { OV9740_IO_OUTPUT_SEL01, 0x00 },
314 { OV9740_IO_OUTPUT_SEL02, 0x00 },
315 { OV9740_IO_CREL00, 0x00 },
316 { OV9740_IO_CREL01, 0x00 },
317 { OV9740_IO_CREL02, 0x00 },
318
319 /* AWB Control */
320 { OV9740_AWB_MANUAL_CTRL, 0x00 },
321
322 /* Analog Control */
323 { OV9740_ANALOG_CTRL03, 0xaa },
324 { OV9740_ANALOG_CTRL32, 0x2f },
325 { OV9740_ANALOG_CTRL20, 0x66 },
326 { OV9740_ANALOG_CTRL21, 0xc0 },
327 { OV9740_ANALOG_CTRL31, 0x52 },
328 { OV9740_ANALOG_CTRL33, 0x50 },
329 { OV9740_ANALOG_CTRL30, 0xca },
330 { OV9740_ANALOG_CTRL04, 0x0c },
331 { OV9740_ANALOG_CTRL01, 0x40 },
332 { OV9740_ANALOG_CTRL02, 0x16 },
333 { OV9740_ANALOG_CTRL10, 0xa1 },
334 { OV9740_ANALOG_CTRL12, 0x24 },
335 { OV9740_ANALOG_CTRL22, 0x9f },
336
337 /* Sensor Control */
338 { OV9740_SENSOR_CTRL03, 0x42 },
339 { OV9740_SENSOR_CTRL04, 0x10 },
340 { OV9740_SENSOR_CTRL05, 0x45 },
341 { OV9740_SENSOR_CTRL07, 0x14 },
342
343 /* Timing Control */
344 { OV9740_TIMING_CTRL33, 0x04 },
345 { OV9740_TIMING_CTRL35, 0x02 },
346 { OV9740_TIMING_CTRL19, 0x6e },
347 { OV9740_TIMING_CTRL17, 0x94 },
348
349 /* AEC/AGC Control */
350 { OV9740_AEC_ENABLE, 0x10 },
351 { OV9740_GAIN_CEILING_01, 0x00 },
352 { OV9740_GAIN_CEILING_02, 0x7f },
353 { OV9740_AEC_HI_THRESHOLD, 0xa0 },
354 { OV9740_AEC_3A1A, 0x05 },
355 { OV9740_AEC_CTRL1B_WPT2, 0x50 },
356 { OV9740_AEC_CTRL0F_WPT, 0x50 },
357 { OV9740_AEC_CTRL10_BPT, 0x4c },
358 { OV9740_AEC_CTRL1E_BPT2, 0x4c },
359 { OV9740_AEC_LO_THRESHOLD, 0x26 },
360
361 /* BLC Control */
362 { OV9740_BLC_AUTO_ENABLE, 0x45 },
363 { OV9740_BLC_MODE, 0x18 },
364
365 /* DVP Control */
366 { OV9740_DVP_VSYNC_CTRL02, 0x04 },
367 { OV9740_DVP_VSYNC_MODE, 0x00 },
368 { OV9740_DVP_VSYNC_CTRL06, 0x08 },
369
370 /* PLL Setting */
371 { OV9740_PLL_MODE_CTRL01, 0x20 },
372 { OV9740_PRE_PLL_CLK_DIV, 0x03 },
373 { OV9740_PLL_MULTIPLIER, 0x4c },
374 { OV9740_VT_SYS_CLK_DIV, 0x01 },
375 { OV9740_VT_PIX_CLK_DIV, 0x08 },
376 { OV9740_PLL_CTRL3010, 0x01 },
377 { OV9740_VFIFO_CTRL00, 0x82 },
378
379 /* Timing Setting */
380 /* VTS */
381 { OV9740_FRM_LENGTH_LN_HI, 0x03 },
382 { OV9740_FRM_LENGTH_LN_LO, 0x07 },
383 /* HTS */
384 { OV9740_LN_LENGTH_PCK_HI, 0x06 },
385 { OV9740_LN_LENGTH_PCK_LO, 0x62 },
386
387 /* MIPI Control */
388 { OV9740_MIPI_CTRL00, 0x44 },
389 { OV9740_MIPI_3837, 0x01 },
390 { OV9740_MIPI_CTRL01, 0x0f },
391 { OV9740_MIPI_CTRL03, 0x05 },
392 { OV9740_MIPI_CTRL05, 0x10 },
393 { OV9740_VFIFO_RD_CTRL, 0x16 },
394 { OV9740_MIPI_CTRL_3012, 0x70 },
395 { OV9740_SC_CMMM_MIPI_CTR, 0x01 },
396};
397
398static const struct ov9740_reg ov9740_regs_vga[] = {
399 { OV9740_X_ADDR_START_HI, 0x00 },
400 { OV9740_X_ADDR_START_LO, 0xa0 },
401 { OV9740_Y_ADDR_START_HI, 0x00 },
402 { OV9740_Y_ADDR_START_LO, 0x00 },
403 { OV9740_X_ADDR_END_HI, 0x04 },
404 { OV9740_X_ADDR_END_LO, 0x63 },
405 { OV9740_Y_ADDR_END_HI, 0x02 },
406 { OV9740_Y_ADDR_END_LO, 0xd3 },
407 { OV9740_X_OUTPUT_SIZE_HI, 0x02 },
408 { OV9740_X_OUTPUT_SIZE_LO, 0x80 },
409 { OV9740_Y_OUTPUT_SIZE_HI, 0x01 },
410 { OV9740_Y_OUTPUT_SIZE_LO, 0xe0 },
411 { OV9740_ISP_CTRL1E, 0x03 },
412 { OV9740_ISP_CTRL1F, 0xc0 },
413 { OV9740_ISP_CTRL20, 0x02 },
414 { OV9740_ISP_CTRL21, 0xd0 },
415 { OV9740_VFIFO_READ_START_HI, 0x01 },
416 { OV9740_VFIFO_READ_START_LO, 0x40 },
417 { OV9740_ISP_CTRL00, 0xff },
418 { OV9740_ISP_CTRL01, 0xff },
419 { OV9740_ISP_CTRL03, 0xff },
420};
421
422static const struct ov9740_reg ov9740_regs_720p[] = {
423 { OV9740_X_ADDR_START_HI, 0x00 },
424 { OV9740_X_ADDR_START_LO, 0x00 },
425 { OV9740_Y_ADDR_START_HI, 0x00 },
426 { OV9740_Y_ADDR_START_LO, 0x00 },
427 { OV9740_X_ADDR_END_HI, 0x05 },
428 { OV9740_X_ADDR_END_LO, 0x03 },
429 { OV9740_Y_ADDR_END_HI, 0x02 },
430 { OV9740_Y_ADDR_END_LO, 0xd3 },
431 { OV9740_X_OUTPUT_SIZE_HI, 0x05 },
432 { OV9740_X_OUTPUT_SIZE_LO, 0x00 },
433 { OV9740_Y_OUTPUT_SIZE_HI, 0x02 },
434 { OV9740_Y_OUTPUT_SIZE_LO, 0xd0 },
435 { OV9740_ISP_CTRL1E, 0x05 },
436 { OV9740_ISP_CTRL1F, 0x00 },
437 { OV9740_ISP_CTRL20, 0x02 },
438 { OV9740_ISP_CTRL21, 0xd0 },
439 { OV9740_VFIFO_READ_START_HI, 0x02 },
440 { OV9740_VFIFO_READ_START_LO, 0x30 },
441 { OV9740_ISP_CTRL00, 0xff },
442 { OV9740_ISP_CTRL01, 0xef },
443 { OV9740_ISP_CTRL03, 0xff },
444};
445
446static enum v4l2_mbus_pixelcode ov9740_codes[] = {
447 V4L2_MBUS_FMT_YUYV8_2X8,
448};
449
450static const struct v4l2_queryctrl ov9740_controls[] = {
451 {
452 .id = V4L2_CID_VFLIP,
453 .type = V4L2_CTRL_TYPE_BOOLEAN,
454 .name = "Flip Vertically",
455 .minimum = 0,
456 .maximum = 1,
457 .step = 1,
458 .default_value = 0,
459 },
460 {
461 .id = V4L2_CID_HFLIP,
462 .type = V4L2_CTRL_TYPE_BOOLEAN,
463 .name = "Flip Horizontally",
464 .minimum = 0,
465 .maximum = 1,
466 .step = 1,
467 .default_value = 0,
468 },
469};
470
471/* read a register */
472static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
473{
474 int ret;
475 struct i2c_msg msg[] = {
476 {
477 .addr = client->addr,
478 .flags = 0,
479 .len = 2,
480 .buf = (u8 *)&reg,
481 },
482 {
483 .addr = client->addr,
484 .flags = I2C_M_RD,
485 .len = 1,
486 .buf = val,
487 },
488 };
489
490 reg = swab16(reg);
491
492 ret = i2c_transfer(client->adapter, msg, 2);
493 if (ret < 0) {
494 dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg);
495 return ret;
496 }
497
498 return 0;
499}
500
501/* write a register */
502static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val)
503{
504 struct i2c_msg msg;
505 struct {
506 u16 reg;
507 u8 val;
508 } __packed buf;
509 int ret;
510
511 reg = swab16(reg);
512
513 buf.reg = reg;
514 buf.val = val;
515
516 msg.addr = client->addr;
517 msg.flags = 0;
518 msg.len = 3;
519 msg.buf = (u8 *)&buf;
520
521 ret = i2c_transfer(client->adapter, &msg, 1);
522 if (ret < 0) {
523 dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg);
524 return ret;
525 }
526
527 return 0;
528}
529
530
531/* Read a register, alter its bits, write it back */
532static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset)
533{
534 u8 val;
535 int ret;
536
537 ret = ov9740_reg_read(client, reg, &val);
538 if (ret < 0) {
539 dev_err(&client->dev,
540 "[Read]-Modify-Write of register %02x failed!\n", reg);
541 return ret;
542 }
543
544 val |= set;
545 val &= ~unset;
546
547 ret = ov9740_reg_write(client, reg, val);
548 if (ret < 0) {
549 dev_err(&client->dev,
550 "Read-Modify-[Write] of register %02x failed!\n", reg);
551 return ret;
552 }
553
554 return 0;
555}
556
557static int ov9740_reg_write_array(struct i2c_client *client,
558 const struct ov9740_reg *regarray,
559 int regarraylen)
560{
561 int i;
562 int ret;
563
564 for (i = 0; i < regarraylen; i++) {
565 ret = ov9740_reg_write(client,
566 regarray[i].reg, regarray[i].val);
567 if (ret < 0)
568 return ret;
569 }
570
571 return 0;
572}
573
574/* Start/Stop streaming from the device */
575static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
576{
577 struct i2c_client *client = v4l2_get_subdevdata(sd);
578 struct ov9740_priv *priv = to_ov9740(sd);
579 int ret;
580
581 /* Program orientation register. */
582 if (priv->flag_vflip)
583 ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0);
584 else
585 ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2);
586 if (ret < 0)
587 return ret;
588
589 if (priv->flag_hflip)
590 ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0);
591 else
592 ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1);
593 if (ret < 0)
594 return ret;
595
596 if (enable) {
597 dev_dbg(&client->dev, "Enabling Streaming\n");
598 /* Start Streaming */
599 ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01);
600
601 } else {
602 dev_dbg(&client->dev, "Disabling Streaming\n");
603 /* Software Reset */
604 ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01);
605 if (!ret)
606 /* Setting Streaming to Standby */
607 ret = ov9740_reg_write(client, OV9740_MODE_SELECT,
608 0x00);
609 }
610
611 return ret;
612}
613
614/* Alter bus settings on camera side */
615static int ov9740_set_bus_param(struct soc_camera_device *icd,
616 unsigned long flags)
617{
618 return 0;
619}
620
621/* Request bus settings on camera side */
622static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd)
623{
624 struct soc_camera_link *icl = to_soc_camera_link(icd);
625
626 unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
627 SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
628 SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
629
630 return soc_camera_apply_sensor_flags(icl, flags);
631}
632
633/* Get status of additional camera capabilities */
634static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
635{
636 struct ov9740_priv *priv = to_ov9740(sd);
637
638 switch (ctrl->id) {
639 case V4L2_CID_VFLIP:
640 ctrl->value = priv->flag_vflip;
641 break;
642 case V4L2_CID_HFLIP:
643 ctrl->value = priv->flag_hflip;
644 break;
645 default:
646 return -EINVAL;
647 }
648
649 return 0;
650}
651
652/* Set status of additional camera capabilities */
653static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
654{
655 struct ov9740_priv *priv = to_ov9740(sd);
656
657 switch (ctrl->id) {
658 case V4L2_CID_VFLIP:
659 priv->flag_vflip = ctrl->value;
660 break;
661 case V4L2_CID_HFLIP:
662 priv->flag_hflip = ctrl->value;
663 break;
664 default:
665 return -EINVAL;
666 }
667
668 return 0;
669}
670
671/* Get chip identification */
672static int ov9740_g_chip_ident(struct v4l2_subdev *sd,
673 struct v4l2_dbg_chip_ident *id)
674{
675 struct ov9740_priv *priv = to_ov9740(sd);
676
677 id->ident = priv->ident;
678 id->revision = priv->revision;
679
680 return 0;
681}
682
683#ifdef CONFIG_VIDEO_ADV_DEBUG
684static int ov9740_get_register(struct v4l2_subdev *sd,
685 struct v4l2_dbg_register *reg)
686{
687 struct i2c_client *client = v4l2_get_subdevdata(sd);
688 int ret;
689 u8 val;
690
691 if (reg->reg & ~0xffff)
692 return -EINVAL;
693
694 reg->size = 2;
695
696 ret = ov9740_reg_read(client, reg->reg, &val);
697 if (ret)
698 return ret;
699
700 reg->val = (__u64)val;
701
702 return ret;
703}
704
705static int ov9740_set_register(struct v4l2_subdev *sd,
706 struct v4l2_dbg_register *reg)
707{
708 struct i2c_client *client = v4l2_get_subdevdata(sd);
709
710 if (reg->reg & ~0xffff || reg->val & ~0xff)
711 return -EINVAL;
712
713 return ov9740_reg_write(client, reg->reg, reg->val);
714}
715#endif
716
717/* select nearest higher resolution for capture */
718static void ov9740_res_roundup(u32 *width, u32 *height)
719{
720 int i;
721
722 for (i = 0; i < ARRAY_SIZE(ov9740_resolutions); i++)
723 if ((ov9740_resolutions[i].width >= *width) &&
724 (ov9740_resolutions[i].height >= *height)) {
725 *width = ov9740_resolutions[i].width;
726 *height = ov9740_resolutions[i].height;
727 return;
728 }
729
730 *width = ov9740_resolutions[OV9740_720P].width;
731 *height = ov9740_resolutions[OV9740_720P].height;
732}
733
734/* Setup registers according to resolution and color encoding */
735static int ov9740_set_res(struct i2c_client *client, u32 width)
736{
737 int ret;
738
739 /* select register configuration for given resolution */
740 if (width == ov9740_resolutions[OV9740_VGA].width) {
741 dev_dbg(&client->dev, "Setting image size to 640x480\n");
742 ret = ov9740_reg_write_array(client, ov9740_regs_vga,
743 ARRAY_SIZE(ov9740_regs_vga));
744 } else if (width == ov9740_resolutions[OV9740_720P].width) {
745 dev_dbg(&client->dev, "Setting image size to 1280x720\n");
746 ret = ov9740_reg_write_array(client, ov9740_regs_720p,
747 ARRAY_SIZE(ov9740_regs_720p));
748 } else {
749 dev_err(&client->dev, "Failed to select resolution!\n");
750 return -EINVAL;
751 }
752
753 return ret;
754}
755
756/* set the format we will capture in */
757static int ov9740_s_fmt(struct v4l2_subdev *sd,
758 struct v4l2_mbus_framefmt *mf)
759{
760 struct i2c_client *client = v4l2_get_subdevdata(sd);
761 enum v4l2_colorspace cspace;
762 enum v4l2_mbus_pixelcode code = mf->code;
763 int ret;
764
765 ov9740_res_roundup(&mf->width, &mf->height);
766
767 switch (code) {
768 case V4L2_MBUS_FMT_YUYV8_2X8:
769 cspace = V4L2_COLORSPACE_SRGB;
770 break;
771 default:
772 return -EINVAL;
773 }
774
775 ret = ov9740_reg_write_array(client, ov9740_defaults,
776 ARRAY_SIZE(ov9740_defaults));
777 if (ret < 0)
778 return ret;
779
780 ret = ov9740_set_res(client, mf->width);
781 if (ret < 0)
782 return ret;
783
784 mf->code = code;
785 mf->colorspace = cspace;
786
787 return ret;
788}
789
790static int ov9740_try_fmt(struct v4l2_subdev *sd,
791 struct v4l2_mbus_framefmt *mf)
792{
793 ov9740_res_roundup(&mf->width, &mf->height);
794
795 mf->field = V4L2_FIELD_NONE;
796 mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
797 mf->colorspace = V4L2_COLORSPACE_SRGB;
798
799 return 0;
800}
801
802static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
803 enum v4l2_mbus_pixelcode *code)
804{
805 if (index >= ARRAY_SIZE(ov9740_codes))
806 return -EINVAL;
807
808 *code = ov9740_codes[index];
809
810 return 0;
811}
812
813static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
814{
815 a->bounds.left = 0;
816 a->bounds.top = 0;
817 a->bounds.width = ov9740_resolutions[OV9740_720P].width;
818 a->bounds.height = ov9740_resolutions[OV9740_720P].height;
819 a->defrect = a->bounds;
820 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
821 a->pixelaspect.numerator = 1;
822 a->pixelaspect.denominator = 1;
823
824 return 0;
825}
826
827static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
828{
829 a->c.left = 0;
830 a->c.top = 0;
831 a->c.width = ov9740_resolutions[OV9740_720P].width;
832 a->c.height = ov9740_resolutions[OV9740_720P].height;
833 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
834
835 return 0;
836}
837
838static int ov9740_video_probe(struct soc_camera_device *icd,
839 struct i2c_client *client)
840{
841 struct v4l2_subdev *sd = i2c_get_clientdata(client);
842 struct ov9740_priv *priv = to_ov9740(sd);
843 u8 modelhi, modello;
844 int ret;
845
846 /*
847 * We must have a parent by now. And it cannot be a wrong one.
848 * So this entire test is completely redundant.
849 */
850 if (!icd->dev.parent ||
851 to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
852 dev_err(&client->dev, "Parent missing or invalid!\n");
853 ret = -ENODEV;
854 goto err;
855 }
856
857 /*
858 * check and show product ID and manufacturer ID
859 */
860 ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi);
861 if (ret < 0)
862 goto err;
863
864 ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello);
865 if (ret < 0)
866 goto err;
867
868 priv->model = (modelhi << 8) | modello;
869
870 ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision);
871 if (ret < 0)
872 goto err;
873
874 ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid);
875 if (ret < 0)
876 goto err;
877
878 ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver);
879 if (ret < 0)
880 goto err;
881
882 if (priv->model != 0x9740) {
883 ret = -ENODEV;
884 goto err;
885 }
886
887 priv->ident = V4L2_IDENT_OV9740;
888
889 dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, "
890 "Manufacturer 0x%02x, SMIA Version 0x%02x\n",
891 priv->model, priv->revision, priv->manid, priv->smiaver);
892
893err:
894 return ret;
895}
896
897static struct soc_camera_ops ov9740_ops = {
898 .set_bus_param = ov9740_set_bus_param,
899 .query_bus_param = ov9740_query_bus_param,
900 .controls = ov9740_controls,
901 .num_controls = ARRAY_SIZE(ov9740_controls),
902};
903
904static struct v4l2_subdev_core_ops ov9740_core_ops = {
905 .g_ctrl = ov9740_g_ctrl,
906 .s_ctrl = ov9740_s_ctrl,
907 .g_chip_ident = ov9740_g_chip_ident,
908#ifdef CONFIG_VIDEO_ADV_DEBUG
909 .g_register = ov9740_get_register,
910 .s_register = ov9740_set_register,
911#endif
912
913};
914
915static struct v4l2_subdev_video_ops ov9740_video_ops = {
916 .s_stream = ov9740_s_stream,
917 .s_mbus_fmt = ov9740_s_fmt,
918 .try_mbus_fmt = ov9740_try_fmt,
919 .enum_mbus_fmt = ov9740_enum_fmt,
920 .cropcap = ov9740_cropcap,
921 .g_crop = ov9740_g_crop,
922};
923
924static struct v4l2_subdev_ops ov9740_subdev_ops = {
925 .core = &ov9740_core_ops,
926 .video = &ov9740_video_ops,
927};
928
929/*
930 * i2c_driver function
931 */
932static int ov9740_probe(struct i2c_client *client,
933 const struct i2c_device_id *did)
934{
935 struct ov9740_priv *priv;
936 struct soc_camera_device *icd = client->dev.platform_data;
937 struct soc_camera_link *icl;
938 int ret;
939
940 if (!icd) {
941 dev_err(&client->dev, "Missing soc-camera data!\n");
942 return -EINVAL;
943 }
944
945 icl = to_soc_camera_link(icd);
946 if (!icl) {
947 dev_err(&client->dev, "Missing platform_data for driver\n");
948 return -EINVAL;
949 }
950
951 priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL);
952 if (!priv) {
953 dev_err(&client->dev, "Failed to allocate private data!\n");
954 return -ENOMEM;
955 }
956
957 v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
958
959 icd->ops = &ov9740_ops;
960
961 ret = ov9740_video_probe(icd, client);
962 if (ret < 0) {
963 icd->ops = NULL;
964 kfree(priv);
965 }
966
967 return ret;
968}
969
970static int ov9740_remove(struct i2c_client *client)
971{
972 struct ov9740_priv *priv = i2c_get_clientdata(client);
973
974 kfree(priv);
975
976 return 0;
977}
978
979static const struct i2c_device_id ov9740_id[] = {
980 { "ov9740", 0 },
981 { }
982};
983MODULE_DEVICE_TABLE(i2c, ov9740_id);
984
985static struct i2c_driver ov9740_i2c_driver = {
986 .driver = {
987 .name = "ov9740",
988 },
989 .probe = ov9740_probe,
990 .remove = ov9740_remove,
991 .id_table = ov9740_id,
992};
993
994static int __init ov9740_module_init(void)
995{
996 return i2c_add_driver(&ov9740_i2c_driver);
997}
998
999static void __exit ov9740_module_exit(void)
1000{
1001 i2c_del_driver(&ov9740_i2c_driver);
1002}
1003
1004module_init(ov9740_module_init);
1005module_exit(ov9740_module_exit);
1006
1007MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740");
1008MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
1009MODULE_LICENSE("GPL v2");