1d953e3cbSShawn Tu // SPDX-License-Identifier: GPL-2.0
2d953e3cbSShawn Tu // Copyright (C) 2021 Intel Corporation
3d953e3cbSShawn Tu
4d953e3cbSShawn Tu #include <linux/acpi.h>
5d953e3cbSShawn Tu #include <linux/delay.h>
6d953e3cbSShawn Tu #include <linux/i2c.h>
7d953e3cbSShawn Tu #include <linux/module.h>
8d953e3cbSShawn Tu #include <linux/pm_runtime.h>
9d953e3cbSShawn Tu #include <media/v4l2-ctrls.h>
10d953e3cbSShawn Tu #include <media/v4l2-device.h>
11d953e3cbSShawn Tu #include <asm/unaligned.h>
12d953e3cbSShawn Tu
13d953e3cbSShawn Tu #define IMX208_REG_MODE_SELECT 0x0100
14d953e3cbSShawn Tu #define IMX208_MODE_STANDBY 0x00
15d953e3cbSShawn Tu #define IMX208_MODE_STREAMING 0x01
16d953e3cbSShawn Tu
17d953e3cbSShawn Tu /* Chip ID */
18d953e3cbSShawn Tu #define IMX208_REG_CHIP_ID 0x0000
19d953e3cbSShawn Tu #define IMX208_CHIP_ID 0x0208
20d953e3cbSShawn Tu
21d953e3cbSShawn Tu /* V_TIMING internal */
22d953e3cbSShawn Tu #define IMX208_REG_VTS 0x0340
23d953e3cbSShawn Tu #define IMX208_VTS_60FPS 0x0472
24d953e3cbSShawn Tu #define IMX208_VTS_BINNING 0x0239
25d953e3cbSShawn Tu #define IMX208_VTS_60FPS_MIN 0x0458
26d953e3cbSShawn Tu #define IMX208_VTS_BINNING_MIN 0x0230
27d953e3cbSShawn Tu #define IMX208_VTS_MAX 0xffff
28d953e3cbSShawn Tu
29d953e3cbSShawn Tu /* HBLANK control - read only */
30d953e3cbSShawn Tu #define IMX208_PPL_384MHZ 2248
31d953e3cbSShawn Tu #define IMX208_PPL_96MHZ 2248
32d953e3cbSShawn Tu
33d953e3cbSShawn Tu /* Exposure control */
34d953e3cbSShawn Tu #define IMX208_REG_EXPOSURE 0x0202
35d953e3cbSShawn Tu #define IMX208_EXPOSURE_MIN 4
36d953e3cbSShawn Tu #define IMX208_EXPOSURE_STEP 1
37d953e3cbSShawn Tu #define IMX208_EXPOSURE_DEFAULT 0x190
38d953e3cbSShawn Tu #define IMX208_EXPOSURE_MAX 65535
39d953e3cbSShawn Tu
40d953e3cbSShawn Tu /* Analog gain control */
41d953e3cbSShawn Tu #define IMX208_REG_ANALOG_GAIN 0x0204
42d953e3cbSShawn Tu #define IMX208_ANA_GAIN_MIN 0
43d953e3cbSShawn Tu #define IMX208_ANA_GAIN_MAX 0x00e0
44d953e3cbSShawn Tu #define IMX208_ANA_GAIN_STEP 1
45d953e3cbSShawn Tu #define IMX208_ANA_GAIN_DEFAULT 0x0
46d953e3cbSShawn Tu
47d953e3cbSShawn Tu /* Digital gain control */
48d953e3cbSShawn Tu #define IMX208_REG_GR_DIGITAL_GAIN 0x020e
49d953e3cbSShawn Tu #define IMX208_REG_R_DIGITAL_GAIN 0x0210
50d953e3cbSShawn Tu #define IMX208_REG_B_DIGITAL_GAIN 0x0212
51d953e3cbSShawn Tu #define IMX208_REG_GB_DIGITAL_GAIN 0x0214
52d953e3cbSShawn Tu #define IMX208_DIGITAL_GAIN_SHIFT 8
53d953e3cbSShawn Tu
54d953e3cbSShawn Tu /* Orientation */
55d953e3cbSShawn Tu #define IMX208_REG_ORIENTATION_CONTROL 0x0101
56d953e3cbSShawn Tu
57d953e3cbSShawn Tu /* Test Pattern Control */
58d953e3cbSShawn Tu #define IMX208_REG_TEST_PATTERN_MODE 0x0600
59d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_DISABLE 0x0
60d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_SOLID_COLOR 0x1
61d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_COLOR_BARS 0x2
62d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_GREY_COLOR 0x3
63d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_PN9 0x4
64d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_FIX_1 0x100
65d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_FIX_2 0x101
66d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_FIX_3 0x102
67d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_FIX_4 0x103
68d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_FIX_5 0x104
69d953e3cbSShawn Tu #define IMX208_TEST_PATTERN_FIX_6 0x105
70d953e3cbSShawn Tu
71d953e3cbSShawn Tu /* OTP Access */
72d953e3cbSShawn Tu #define IMX208_OTP_BASE 0x3500
73d953e3cbSShawn Tu #define IMX208_OTP_SIZE 40
74d953e3cbSShawn Tu
75d953e3cbSShawn Tu struct imx208_reg {
76d953e3cbSShawn Tu u16 address;
77d953e3cbSShawn Tu u8 val;
78d953e3cbSShawn Tu };
79d953e3cbSShawn Tu
80d953e3cbSShawn Tu struct imx208_reg_list {
81d953e3cbSShawn Tu u32 num_of_regs;
82d953e3cbSShawn Tu const struct imx208_reg *regs;
83d953e3cbSShawn Tu };
84d953e3cbSShawn Tu
85d953e3cbSShawn Tu /* Link frequency config */
86d953e3cbSShawn Tu struct imx208_link_freq_config {
87d953e3cbSShawn Tu u32 pixels_per_line;
88d953e3cbSShawn Tu
89d953e3cbSShawn Tu /* PLL registers for this link frequency */
90d953e3cbSShawn Tu struct imx208_reg_list reg_list;
91d953e3cbSShawn Tu };
92d953e3cbSShawn Tu
93d953e3cbSShawn Tu /* Mode : resolution and related config&values */
94d953e3cbSShawn Tu struct imx208_mode {
95d953e3cbSShawn Tu /* Frame width */
96d953e3cbSShawn Tu u32 width;
97d953e3cbSShawn Tu /* Frame height */
98d953e3cbSShawn Tu u32 height;
99d953e3cbSShawn Tu
100d953e3cbSShawn Tu /* V-timing */
101d953e3cbSShawn Tu u32 vts_def;
102d953e3cbSShawn Tu u32 vts_min;
103d953e3cbSShawn Tu
104d953e3cbSShawn Tu /* Index of Link frequency config to be used */
105d953e3cbSShawn Tu u32 link_freq_index;
106d953e3cbSShawn Tu /* Default register values */
107d953e3cbSShawn Tu struct imx208_reg_list reg_list;
108d953e3cbSShawn Tu };
109d953e3cbSShawn Tu
110d953e3cbSShawn Tu static const struct imx208_reg pll_ctrl_reg[] = {
111d953e3cbSShawn Tu {0x0305, 0x02},
112d953e3cbSShawn Tu {0x0307, 0x50},
113d953e3cbSShawn Tu {0x303C, 0x3C},
114d953e3cbSShawn Tu };
115d953e3cbSShawn Tu
116d953e3cbSShawn Tu static const struct imx208_reg mode_1936x1096_60fps_regs[] = {
117d953e3cbSShawn Tu {0x0340, 0x04},
118d953e3cbSShawn Tu {0x0341, 0x72},
119d953e3cbSShawn Tu {0x0342, 0x04},
120d953e3cbSShawn Tu {0x0343, 0x64},
121d953e3cbSShawn Tu {0x034C, 0x07},
122d953e3cbSShawn Tu {0x034D, 0x90},
123d953e3cbSShawn Tu {0x034E, 0x04},
124d953e3cbSShawn Tu {0x034F, 0x48},
125d953e3cbSShawn Tu {0x0381, 0x01},
126d953e3cbSShawn Tu {0x0383, 0x01},
127d953e3cbSShawn Tu {0x0385, 0x01},
128d953e3cbSShawn Tu {0x0387, 0x01},
129d953e3cbSShawn Tu {0x3048, 0x00},
130d953e3cbSShawn Tu {0x3050, 0x01},
131d953e3cbSShawn Tu {0x30D5, 0x00},
132d953e3cbSShawn Tu {0x3301, 0x00},
133d953e3cbSShawn Tu {0x3318, 0x62},
134d953e3cbSShawn Tu {0x0202, 0x01},
135d953e3cbSShawn Tu {0x0203, 0x90},
136d953e3cbSShawn Tu {0x0205, 0x00},
137d953e3cbSShawn Tu };
138d953e3cbSShawn Tu
139d953e3cbSShawn Tu static const struct imx208_reg mode_968_548_60fps_regs[] = {
140d953e3cbSShawn Tu {0x0340, 0x02},
141d953e3cbSShawn Tu {0x0341, 0x39},
142d953e3cbSShawn Tu {0x0342, 0x08},
143d953e3cbSShawn Tu {0x0343, 0xC8},
144d953e3cbSShawn Tu {0x034C, 0x03},
145d953e3cbSShawn Tu {0x034D, 0xC8},
146d953e3cbSShawn Tu {0x034E, 0x02},
147d953e3cbSShawn Tu {0x034F, 0x24},
148d953e3cbSShawn Tu {0x0381, 0x01},
149d953e3cbSShawn Tu {0x0383, 0x03},
150d953e3cbSShawn Tu {0x0385, 0x01},
151d953e3cbSShawn Tu {0x0387, 0x03},
152d953e3cbSShawn Tu {0x3048, 0x01},
153d953e3cbSShawn Tu {0x3050, 0x02},
154d953e3cbSShawn Tu {0x30D5, 0x03},
155d953e3cbSShawn Tu {0x3301, 0x10},
156d953e3cbSShawn Tu {0x3318, 0x75},
157d953e3cbSShawn Tu {0x0202, 0x01},
158d953e3cbSShawn Tu {0x0203, 0x90},
159d953e3cbSShawn Tu {0x0205, 0x00},
160d953e3cbSShawn Tu };
161d953e3cbSShawn Tu
162d953e3cbSShawn Tu static const s64 imx208_discrete_digital_gain[] = {
163d953e3cbSShawn Tu 1, 2, 4, 8, 16,
164d953e3cbSShawn Tu };
165d953e3cbSShawn Tu
166d953e3cbSShawn Tu static const char * const imx208_test_pattern_menu[] = {
167d953e3cbSShawn Tu "Disabled",
168d953e3cbSShawn Tu "Solid Color",
169d953e3cbSShawn Tu "100% Color Bar",
170d953e3cbSShawn Tu "Fade to Grey Color Bar",
171d953e3cbSShawn Tu "PN9",
172d953e3cbSShawn Tu "Fixed Pattern1",
173d953e3cbSShawn Tu "Fixed Pattern2",
174d953e3cbSShawn Tu "Fixed Pattern3",
175d953e3cbSShawn Tu "Fixed Pattern4",
176d953e3cbSShawn Tu "Fixed Pattern5",
177d953e3cbSShawn Tu "Fixed Pattern6"
178d953e3cbSShawn Tu };
179d953e3cbSShawn Tu
180d953e3cbSShawn Tu static const int imx208_test_pattern_val[] = {
181d953e3cbSShawn Tu IMX208_TEST_PATTERN_DISABLE,
182d953e3cbSShawn Tu IMX208_TEST_PATTERN_SOLID_COLOR,
183d953e3cbSShawn Tu IMX208_TEST_PATTERN_COLOR_BARS,
184d953e3cbSShawn Tu IMX208_TEST_PATTERN_GREY_COLOR,
185d953e3cbSShawn Tu IMX208_TEST_PATTERN_PN9,
186d953e3cbSShawn Tu IMX208_TEST_PATTERN_FIX_1,
187d953e3cbSShawn Tu IMX208_TEST_PATTERN_FIX_2,
188d953e3cbSShawn Tu IMX208_TEST_PATTERN_FIX_3,
189d953e3cbSShawn Tu IMX208_TEST_PATTERN_FIX_4,
190d953e3cbSShawn Tu IMX208_TEST_PATTERN_FIX_5,
191d953e3cbSShawn Tu IMX208_TEST_PATTERN_FIX_6,
192d953e3cbSShawn Tu };
193d953e3cbSShawn Tu
194d953e3cbSShawn Tu /* Configurations for supported link frequencies */
195d953e3cbSShawn Tu #define IMX208_MHZ (1000 * 1000ULL)
196d953e3cbSShawn Tu #define IMX208_LINK_FREQ_384MHZ (384ULL * IMX208_MHZ)
197d953e3cbSShawn Tu #define IMX208_LINK_FREQ_96MHZ (96ULL * IMX208_MHZ)
198d953e3cbSShawn Tu
199d953e3cbSShawn Tu #define IMX208_DATA_RATE_DOUBLE 2
200d953e3cbSShawn Tu #define IMX208_NUM_OF_LANES 2
201d953e3cbSShawn Tu #define IMX208_PIXEL_BITS 10
202d953e3cbSShawn Tu
203d953e3cbSShawn Tu enum {
204d953e3cbSShawn Tu IMX208_LINK_FREQ_384MHZ_INDEX,
205d953e3cbSShawn Tu IMX208_LINK_FREQ_96MHZ_INDEX,
206d953e3cbSShawn Tu };
207d953e3cbSShawn Tu
208d953e3cbSShawn Tu /*
209d953e3cbSShawn Tu * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
210d953e3cbSShawn Tu * data rate => double data rate; number of lanes => 2; bits per pixel => 10
211d953e3cbSShawn Tu */
link_freq_to_pixel_rate(u64 f)212d953e3cbSShawn Tu static u64 link_freq_to_pixel_rate(u64 f)
213d953e3cbSShawn Tu {
214d953e3cbSShawn Tu f *= IMX208_DATA_RATE_DOUBLE * IMX208_NUM_OF_LANES;
215d953e3cbSShawn Tu do_div(f, IMX208_PIXEL_BITS);
216d953e3cbSShawn Tu
217d953e3cbSShawn Tu return f;
218d953e3cbSShawn Tu }
219d953e3cbSShawn Tu
220d953e3cbSShawn Tu /* Menu items for LINK_FREQ V4L2 control */
221d953e3cbSShawn Tu static const s64 link_freq_menu_items[] = {
222d953e3cbSShawn Tu [IMX208_LINK_FREQ_384MHZ_INDEX] = IMX208_LINK_FREQ_384MHZ,
223d953e3cbSShawn Tu [IMX208_LINK_FREQ_96MHZ_INDEX] = IMX208_LINK_FREQ_96MHZ,
224d953e3cbSShawn Tu };
225d953e3cbSShawn Tu
226d953e3cbSShawn Tu /* Link frequency configs */
227d953e3cbSShawn Tu static const struct imx208_link_freq_config link_freq_configs[] = {
228d953e3cbSShawn Tu [IMX208_LINK_FREQ_384MHZ_INDEX] = {
229d953e3cbSShawn Tu .pixels_per_line = IMX208_PPL_384MHZ,
230d953e3cbSShawn Tu .reg_list = {
231d953e3cbSShawn Tu .num_of_regs = ARRAY_SIZE(pll_ctrl_reg),
232d953e3cbSShawn Tu .regs = pll_ctrl_reg,
233d953e3cbSShawn Tu }
234d953e3cbSShawn Tu },
235d953e3cbSShawn Tu [IMX208_LINK_FREQ_96MHZ_INDEX] = {
236d953e3cbSShawn Tu .pixels_per_line = IMX208_PPL_96MHZ,
237d953e3cbSShawn Tu .reg_list = {
238d953e3cbSShawn Tu .num_of_regs = ARRAY_SIZE(pll_ctrl_reg),
239d953e3cbSShawn Tu .regs = pll_ctrl_reg,
240d953e3cbSShawn Tu }
241d953e3cbSShawn Tu },
242d953e3cbSShawn Tu };
243d953e3cbSShawn Tu
244d953e3cbSShawn Tu /* Mode configs */
245d953e3cbSShawn Tu static const struct imx208_mode supported_modes[] = {
246d953e3cbSShawn Tu {
247d953e3cbSShawn Tu .width = 1936,
248d953e3cbSShawn Tu .height = 1096,
249d953e3cbSShawn Tu .vts_def = IMX208_VTS_60FPS,
250d953e3cbSShawn Tu .vts_min = IMX208_VTS_60FPS_MIN,
251d953e3cbSShawn Tu .reg_list = {
252d953e3cbSShawn Tu .num_of_regs = ARRAY_SIZE(mode_1936x1096_60fps_regs),
253d953e3cbSShawn Tu .regs = mode_1936x1096_60fps_regs,
254d953e3cbSShawn Tu },
255d953e3cbSShawn Tu .link_freq_index = IMX208_LINK_FREQ_384MHZ_INDEX,
256d953e3cbSShawn Tu },
257d953e3cbSShawn Tu {
258d953e3cbSShawn Tu .width = 968,
259d953e3cbSShawn Tu .height = 548,
260d953e3cbSShawn Tu .vts_def = IMX208_VTS_BINNING,
261d953e3cbSShawn Tu .vts_min = IMX208_VTS_BINNING_MIN,
262d953e3cbSShawn Tu .reg_list = {
263d953e3cbSShawn Tu .num_of_regs = ARRAY_SIZE(mode_968_548_60fps_regs),
264d953e3cbSShawn Tu .regs = mode_968_548_60fps_regs,
265d953e3cbSShawn Tu },
266d953e3cbSShawn Tu .link_freq_index = IMX208_LINK_FREQ_96MHZ_INDEX,
267d953e3cbSShawn Tu },
268d953e3cbSShawn Tu };
269d953e3cbSShawn Tu
270d953e3cbSShawn Tu struct imx208 {
271d953e3cbSShawn Tu struct v4l2_subdev sd;
272d953e3cbSShawn Tu struct media_pad pad;
273d953e3cbSShawn Tu
274d953e3cbSShawn Tu struct v4l2_ctrl_handler ctrl_handler;
275d953e3cbSShawn Tu /* V4L2 Controls */
276d953e3cbSShawn Tu struct v4l2_ctrl *link_freq;
277d953e3cbSShawn Tu struct v4l2_ctrl *pixel_rate;
278d953e3cbSShawn Tu struct v4l2_ctrl *vblank;
279d953e3cbSShawn Tu struct v4l2_ctrl *hblank;
280d953e3cbSShawn Tu struct v4l2_ctrl *vflip;
281d953e3cbSShawn Tu struct v4l2_ctrl *hflip;
282d953e3cbSShawn Tu
283d953e3cbSShawn Tu /* Current mode */
284d953e3cbSShawn Tu const struct imx208_mode *cur_mode;
285d953e3cbSShawn Tu
286d953e3cbSShawn Tu /*
287d953e3cbSShawn Tu * Mutex for serialized access:
288d953e3cbSShawn Tu * Protect sensor set pad format and start/stop streaming safely.
289d953e3cbSShawn Tu * Protect access to sensor v4l2 controls.
290d953e3cbSShawn Tu */
291d953e3cbSShawn Tu struct mutex imx208_mx;
292d953e3cbSShawn Tu
293d953e3cbSShawn Tu /* Streaming on/off */
294d953e3cbSShawn Tu bool streaming;
295d953e3cbSShawn Tu
296d953e3cbSShawn Tu /* OTP data */
297d953e3cbSShawn Tu bool otp_read;
298d953e3cbSShawn Tu char otp_data[IMX208_OTP_SIZE];
29956ca3be8SBingbu Cao
30056ca3be8SBingbu Cao /* True if the device has been identified */
30156ca3be8SBingbu Cao bool identified;
302d953e3cbSShawn Tu };
303d953e3cbSShawn Tu
to_imx208(struct v4l2_subdev * _sd)304d953e3cbSShawn Tu static inline struct imx208 *to_imx208(struct v4l2_subdev *_sd)
305d953e3cbSShawn Tu {
306d953e3cbSShawn Tu return container_of(_sd, struct imx208, sd);
307d953e3cbSShawn Tu }
308d953e3cbSShawn Tu
309d953e3cbSShawn Tu /* Get bayer order based on flip setting. */
imx208_get_format_code(struct imx208 * imx208)310d953e3cbSShawn Tu static u32 imx208_get_format_code(struct imx208 *imx208)
311d953e3cbSShawn Tu {
312d953e3cbSShawn Tu /*
313d953e3cbSShawn Tu * Only one bayer order is supported.
314d953e3cbSShawn Tu * It depends on the flip settings.
315d953e3cbSShawn Tu */
316d953e3cbSShawn Tu static const u32 codes[2][2] = {
317d953e3cbSShawn Tu { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
318d953e3cbSShawn Tu { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
319d953e3cbSShawn Tu };
320d953e3cbSShawn Tu
321d953e3cbSShawn Tu return codes[imx208->vflip->val][imx208->hflip->val];
322d953e3cbSShawn Tu }
323d953e3cbSShawn Tu
324d953e3cbSShawn Tu /* Read registers up to 4 at a time */
imx208_read_reg(struct imx208 * imx208,u16 reg,u32 len,u32 * val)325d953e3cbSShawn Tu static int imx208_read_reg(struct imx208 *imx208, u16 reg, u32 len, u32 *val)
326d953e3cbSShawn Tu {
327d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
328d953e3cbSShawn Tu struct i2c_msg msgs[2];
329d953e3cbSShawn Tu u8 addr_buf[2] = { reg >> 8, reg & 0xff };
330d953e3cbSShawn Tu u8 data_buf[4] = { 0, };
331d953e3cbSShawn Tu int ret;
332d953e3cbSShawn Tu
333d953e3cbSShawn Tu if (len > 4)
334d953e3cbSShawn Tu return -EINVAL;
335d953e3cbSShawn Tu
336d953e3cbSShawn Tu /* Write register address */
337d953e3cbSShawn Tu msgs[0].addr = client->addr;
338d953e3cbSShawn Tu msgs[0].flags = 0;
339d953e3cbSShawn Tu msgs[0].len = ARRAY_SIZE(addr_buf);
340d953e3cbSShawn Tu msgs[0].buf = addr_buf;
341d953e3cbSShawn Tu
342d953e3cbSShawn Tu /* Read data from register */
343d953e3cbSShawn Tu msgs[1].addr = client->addr;
344d953e3cbSShawn Tu msgs[1].flags = I2C_M_RD;
345d953e3cbSShawn Tu msgs[1].len = len;
346d953e3cbSShawn Tu msgs[1].buf = &data_buf[4 - len];
347d953e3cbSShawn Tu
348d953e3cbSShawn Tu ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
349d953e3cbSShawn Tu if (ret != ARRAY_SIZE(msgs))
350d953e3cbSShawn Tu return -EIO;
351d953e3cbSShawn Tu
352d953e3cbSShawn Tu *val = get_unaligned_be32(data_buf);
353d953e3cbSShawn Tu
354d953e3cbSShawn Tu return 0;
355d953e3cbSShawn Tu }
356d953e3cbSShawn Tu
357d953e3cbSShawn Tu /* Write registers up to 4 at a time */
imx208_write_reg(struct imx208 * imx208,u16 reg,u32 len,u32 val)358d953e3cbSShawn Tu static int imx208_write_reg(struct imx208 *imx208, u16 reg, u32 len, u32 val)
359d953e3cbSShawn Tu {
360d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
361d953e3cbSShawn Tu u8 buf[6];
362d953e3cbSShawn Tu
363d953e3cbSShawn Tu if (len > 4)
364d953e3cbSShawn Tu return -EINVAL;
365d953e3cbSShawn Tu
366d953e3cbSShawn Tu put_unaligned_be16(reg, buf);
367d953e3cbSShawn Tu put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
368d953e3cbSShawn Tu if (i2c_master_send(client, buf, len + 2) != len + 2)
369d953e3cbSShawn Tu return -EIO;
370d953e3cbSShawn Tu
371d953e3cbSShawn Tu return 0;
372d953e3cbSShawn Tu }
373d953e3cbSShawn Tu
374d953e3cbSShawn Tu /* Write a list of registers */
imx208_write_regs(struct imx208 * imx208,const struct imx208_reg * regs,u32 len)375d953e3cbSShawn Tu static int imx208_write_regs(struct imx208 *imx208,
376d953e3cbSShawn Tu const struct imx208_reg *regs, u32 len)
377d953e3cbSShawn Tu {
378d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
379d953e3cbSShawn Tu unsigned int i;
380d953e3cbSShawn Tu int ret;
381d953e3cbSShawn Tu
382d953e3cbSShawn Tu for (i = 0; i < len; i++) {
383d953e3cbSShawn Tu ret = imx208_write_reg(imx208, regs[i].address, 1,
384d953e3cbSShawn Tu regs[i].val);
385d953e3cbSShawn Tu if (ret) {
386d953e3cbSShawn Tu dev_err_ratelimited(&client->dev,
387d953e3cbSShawn Tu "Failed to write reg 0x%4.4x. error = %d\n",
388d953e3cbSShawn Tu regs[i].address, ret);
389d953e3cbSShawn Tu
390d953e3cbSShawn Tu return ret;
391d953e3cbSShawn Tu }
392d953e3cbSShawn Tu }
393d953e3cbSShawn Tu
394d953e3cbSShawn Tu return 0;
395d953e3cbSShawn Tu }
396d953e3cbSShawn Tu
397d953e3cbSShawn Tu /* Open sub-device */
imx208_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)398d953e3cbSShawn Tu static int imx208_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
399d953e3cbSShawn Tu {
400d953e3cbSShawn Tu struct v4l2_mbus_framefmt *try_fmt =
4010d346d2aSTomi Valkeinen v4l2_subdev_get_try_format(sd, fh->state, 0);
402d953e3cbSShawn Tu
403d953e3cbSShawn Tu /* Initialize try_fmt */
404d953e3cbSShawn Tu try_fmt->width = supported_modes[0].width;
405d953e3cbSShawn Tu try_fmt->height = supported_modes[0].height;
406d953e3cbSShawn Tu try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
407d953e3cbSShawn Tu try_fmt->field = V4L2_FIELD_NONE;
408d953e3cbSShawn Tu
409d953e3cbSShawn Tu return 0;
410d953e3cbSShawn Tu }
411d953e3cbSShawn Tu
imx208_update_digital_gain(struct imx208 * imx208,u32 len,u32 val)412d953e3cbSShawn Tu static int imx208_update_digital_gain(struct imx208 *imx208, u32 len, u32 val)
413d953e3cbSShawn Tu {
414d953e3cbSShawn Tu int ret;
415d953e3cbSShawn Tu
416d953e3cbSShawn Tu val = imx208_discrete_digital_gain[val] << IMX208_DIGITAL_GAIN_SHIFT;
417d953e3cbSShawn Tu
418d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_GR_DIGITAL_GAIN, 2, val);
419d953e3cbSShawn Tu if (ret)
420d953e3cbSShawn Tu return ret;
421d953e3cbSShawn Tu
422d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_GB_DIGITAL_GAIN, 2, val);
423d953e3cbSShawn Tu if (ret)
424d953e3cbSShawn Tu return ret;
425d953e3cbSShawn Tu
426d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_R_DIGITAL_GAIN, 2, val);
427d953e3cbSShawn Tu if (ret)
428d953e3cbSShawn Tu return ret;
429d953e3cbSShawn Tu
430d953e3cbSShawn Tu return imx208_write_reg(imx208, IMX208_REG_B_DIGITAL_GAIN, 2, val);
431d953e3cbSShawn Tu }
432d953e3cbSShawn Tu
imx208_set_ctrl(struct v4l2_ctrl * ctrl)433d953e3cbSShawn Tu static int imx208_set_ctrl(struct v4l2_ctrl *ctrl)
434d953e3cbSShawn Tu {
435d953e3cbSShawn Tu struct imx208 *imx208 =
436d953e3cbSShawn Tu container_of(ctrl->handler, struct imx208, ctrl_handler);
437d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
438d953e3cbSShawn Tu int ret;
439d953e3cbSShawn Tu
440d953e3cbSShawn Tu /*
441d953e3cbSShawn Tu * Applying V4L2 control value only happens
442d953e3cbSShawn Tu * when power is up for streaming
443d953e3cbSShawn Tu */
444d953e3cbSShawn Tu if (!pm_runtime_get_if_in_use(&client->dev))
445d953e3cbSShawn Tu return 0;
446d953e3cbSShawn Tu
447d953e3cbSShawn Tu switch (ctrl->id) {
448d953e3cbSShawn Tu case V4L2_CID_ANALOGUE_GAIN:
449d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_ANALOG_GAIN,
450d953e3cbSShawn Tu 2, ctrl->val);
451d953e3cbSShawn Tu break;
452d953e3cbSShawn Tu case V4L2_CID_EXPOSURE:
453d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_EXPOSURE,
454d953e3cbSShawn Tu 2, ctrl->val);
455d953e3cbSShawn Tu break;
456d953e3cbSShawn Tu case V4L2_CID_DIGITAL_GAIN:
457d953e3cbSShawn Tu ret = imx208_update_digital_gain(imx208, 2, ctrl->val);
458d953e3cbSShawn Tu break;
459d953e3cbSShawn Tu case V4L2_CID_VBLANK:
460d953e3cbSShawn Tu /* Update VTS that meets expected vertical blanking */
461d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_VTS, 2,
462d953e3cbSShawn Tu imx208->cur_mode->height + ctrl->val);
463d953e3cbSShawn Tu break;
464d953e3cbSShawn Tu case V4L2_CID_TEST_PATTERN:
465d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_TEST_PATTERN_MODE,
466d953e3cbSShawn Tu 2, imx208_test_pattern_val[ctrl->val]);
467d953e3cbSShawn Tu break;
468d953e3cbSShawn Tu case V4L2_CID_HFLIP:
469d953e3cbSShawn Tu case V4L2_CID_VFLIP:
470d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_ORIENTATION_CONTROL,
471d953e3cbSShawn Tu 1,
472d953e3cbSShawn Tu imx208->hflip->val |
473d953e3cbSShawn Tu imx208->vflip->val << 1);
474d953e3cbSShawn Tu break;
475d953e3cbSShawn Tu default:
476d953e3cbSShawn Tu ret = -EINVAL;
477d953e3cbSShawn Tu dev_err(&client->dev,
478d953e3cbSShawn Tu "ctrl(id:0x%x,val:0x%x) is not handled\n",
479d953e3cbSShawn Tu ctrl->id, ctrl->val);
480d953e3cbSShawn Tu break;
481d953e3cbSShawn Tu }
482d953e3cbSShawn Tu
483d953e3cbSShawn Tu pm_runtime_put(&client->dev);
484d953e3cbSShawn Tu
485d953e3cbSShawn Tu return ret;
486d953e3cbSShawn Tu }
487d953e3cbSShawn Tu
488d953e3cbSShawn Tu static const struct v4l2_ctrl_ops imx208_ctrl_ops = {
489d953e3cbSShawn Tu .s_ctrl = imx208_set_ctrl,
490d953e3cbSShawn Tu };
491d953e3cbSShawn Tu
492d953e3cbSShawn Tu static const struct v4l2_ctrl_config imx208_digital_gain_control = {
493d953e3cbSShawn Tu .ops = &imx208_ctrl_ops,
494d953e3cbSShawn Tu .id = V4L2_CID_DIGITAL_GAIN,
495d953e3cbSShawn Tu .name = "Digital Gain",
496d953e3cbSShawn Tu .type = V4L2_CTRL_TYPE_INTEGER_MENU,
497d953e3cbSShawn Tu .min = 0,
498d953e3cbSShawn Tu .max = ARRAY_SIZE(imx208_discrete_digital_gain) - 1,
499d953e3cbSShawn Tu .step = 0,
500d953e3cbSShawn Tu .def = 0,
501d953e3cbSShawn Tu .menu_skip_mask = 0,
502d953e3cbSShawn Tu .qmenu_int = imx208_discrete_digital_gain,
503d953e3cbSShawn Tu };
504d953e3cbSShawn Tu
imx208_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)505d953e3cbSShawn Tu static int imx208_enum_mbus_code(struct v4l2_subdev *sd,
5060d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
507d953e3cbSShawn Tu struct v4l2_subdev_mbus_code_enum *code)
508d953e3cbSShawn Tu {
509d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
510d953e3cbSShawn Tu
511d953e3cbSShawn Tu if (code->index > 0)
512d953e3cbSShawn Tu return -EINVAL;
513d953e3cbSShawn Tu
514d953e3cbSShawn Tu code->code = imx208_get_format_code(imx208);
515d953e3cbSShawn Tu
516d953e3cbSShawn Tu return 0;
517d953e3cbSShawn Tu }
518d953e3cbSShawn Tu
imx208_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)519d953e3cbSShawn Tu static int imx208_enum_frame_size(struct v4l2_subdev *sd,
5200d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
521d953e3cbSShawn Tu struct v4l2_subdev_frame_size_enum *fse)
522d953e3cbSShawn Tu {
523d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
524d953e3cbSShawn Tu
525d953e3cbSShawn Tu if (fse->index >= ARRAY_SIZE(supported_modes))
526d953e3cbSShawn Tu return -EINVAL;
527d953e3cbSShawn Tu
528d953e3cbSShawn Tu if (fse->code != imx208_get_format_code(imx208))
529d953e3cbSShawn Tu return -EINVAL;
530d953e3cbSShawn Tu
531d953e3cbSShawn Tu fse->min_width = supported_modes[fse->index].width;
532d953e3cbSShawn Tu fse->max_width = fse->min_width;
533d953e3cbSShawn Tu fse->min_height = supported_modes[fse->index].height;
534d953e3cbSShawn Tu fse->max_height = fse->min_height;
535d953e3cbSShawn Tu
536d953e3cbSShawn Tu return 0;
537d953e3cbSShawn Tu }
538d953e3cbSShawn Tu
imx208_mode_to_pad_format(struct imx208 * imx208,const struct imx208_mode * mode,struct v4l2_subdev_format * fmt)539d953e3cbSShawn Tu static void imx208_mode_to_pad_format(struct imx208 *imx208,
540d953e3cbSShawn Tu const struct imx208_mode *mode,
541d953e3cbSShawn Tu struct v4l2_subdev_format *fmt)
542d953e3cbSShawn Tu {
543d953e3cbSShawn Tu fmt->format.width = mode->width;
544d953e3cbSShawn Tu fmt->format.height = mode->height;
545d953e3cbSShawn Tu fmt->format.code = imx208_get_format_code(imx208);
546d953e3cbSShawn Tu fmt->format.field = V4L2_FIELD_NONE;
547d953e3cbSShawn Tu }
548d953e3cbSShawn Tu
__imx208_get_pad_format(struct imx208 * imx208,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)549d953e3cbSShawn Tu static int __imx208_get_pad_format(struct imx208 *imx208,
5500d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
551d953e3cbSShawn Tu struct v4l2_subdev_format *fmt)
552d953e3cbSShawn Tu {
553d953e3cbSShawn Tu if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
5540d346d2aSTomi Valkeinen fmt->format = *v4l2_subdev_get_try_format(&imx208->sd,
5550d346d2aSTomi Valkeinen sd_state,
556d953e3cbSShawn Tu fmt->pad);
557d953e3cbSShawn Tu else
558d953e3cbSShawn Tu imx208_mode_to_pad_format(imx208, imx208->cur_mode, fmt);
559d953e3cbSShawn Tu
560d953e3cbSShawn Tu return 0;
561d953e3cbSShawn Tu }
562d953e3cbSShawn Tu
imx208_get_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)563d953e3cbSShawn Tu static int imx208_get_pad_format(struct v4l2_subdev *sd,
5640d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
565d953e3cbSShawn Tu struct v4l2_subdev_format *fmt)
566d953e3cbSShawn Tu {
567d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
568d953e3cbSShawn Tu int ret;
569d953e3cbSShawn Tu
570d953e3cbSShawn Tu mutex_lock(&imx208->imx208_mx);
5710d346d2aSTomi Valkeinen ret = __imx208_get_pad_format(imx208, sd_state, fmt);
572d953e3cbSShawn Tu mutex_unlock(&imx208->imx208_mx);
573d953e3cbSShawn Tu
574d953e3cbSShawn Tu return ret;
575d953e3cbSShawn Tu }
576d953e3cbSShawn Tu
imx208_set_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)577d953e3cbSShawn Tu static int imx208_set_pad_format(struct v4l2_subdev *sd,
5780d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
579d953e3cbSShawn Tu struct v4l2_subdev_format *fmt)
580d953e3cbSShawn Tu {
581d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
582d953e3cbSShawn Tu const struct imx208_mode *mode;
583d953e3cbSShawn Tu s32 vblank_def;
584d953e3cbSShawn Tu s32 vblank_min;
585d953e3cbSShawn Tu s64 h_blank;
586d953e3cbSShawn Tu s64 pixel_rate;
587d953e3cbSShawn Tu s64 link_freq;
588d953e3cbSShawn Tu
589d953e3cbSShawn Tu mutex_lock(&imx208->imx208_mx);
590d953e3cbSShawn Tu
591d953e3cbSShawn Tu fmt->format.code = imx208_get_format_code(imx208);
592d953e3cbSShawn Tu mode = v4l2_find_nearest_size(supported_modes,
593d953e3cbSShawn Tu ARRAY_SIZE(supported_modes), width, height,
594d953e3cbSShawn Tu fmt->format.width, fmt->format.height);
595d953e3cbSShawn Tu imx208_mode_to_pad_format(imx208, mode, fmt);
596d953e3cbSShawn Tu if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
5970d346d2aSTomi Valkeinen *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format;
598d953e3cbSShawn Tu } else {
599d953e3cbSShawn Tu imx208->cur_mode = mode;
600d953e3cbSShawn Tu __v4l2_ctrl_s_ctrl(imx208->link_freq, mode->link_freq_index);
601d953e3cbSShawn Tu link_freq = link_freq_menu_items[mode->link_freq_index];
602d953e3cbSShawn Tu pixel_rate = link_freq_to_pixel_rate(link_freq);
603d953e3cbSShawn Tu __v4l2_ctrl_s_ctrl_int64(imx208->pixel_rate, pixel_rate);
604d953e3cbSShawn Tu /* Update limits and set FPS to default */
605d953e3cbSShawn Tu vblank_def = imx208->cur_mode->vts_def -
606d953e3cbSShawn Tu imx208->cur_mode->height;
607d953e3cbSShawn Tu vblank_min = imx208->cur_mode->vts_min -
608d953e3cbSShawn Tu imx208->cur_mode->height;
609d953e3cbSShawn Tu __v4l2_ctrl_modify_range(imx208->vblank, vblank_min,
610d953e3cbSShawn Tu IMX208_VTS_MAX - imx208->cur_mode->height,
611d953e3cbSShawn Tu 1, vblank_def);
612d953e3cbSShawn Tu __v4l2_ctrl_s_ctrl(imx208->vblank, vblank_def);
613d953e3cbSShawn Tu h_blank =
614d953e3cbSShawn Tu link_freq_configs[mode->link_freq_index].pixels_per_line
615d953e3cbSShawn Tu - imx208->cur_mode->width;
616d953e3cbSShawn Tu __v4l2_ctrl_modify_range(imx208->hblank, h_blank,
617d953e3cbSShawn Tu h_blank, 1, h_blank);
618d953e3cbSShawn Tu }
619d953e3cbSShawn Tu
620d953e3cbSShawn Tu mutex_unlock(&imx208->imx208_mx);
621d953e3cbSShawn Tu
622d953e3cbSShawn Tu return 0;
623d953e3cbSShawn Tu }
624d953e3cbSShawn Tu
imx208_identify_module(struct imx208 * imx208)62556ca3be8SBingbu Cao static int imx208_identify_module(struct imx208 *imx208)
62656ca3be8SBingbu Cao {
62756ca3be8SBingbu Cao struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
62856ca3be8SBingbu Cao int ret;
62956ca3be8SBingbu Cao u32 val;
63056ca3be8SBingbu Cao
63156ca3be8SBingbu Cao if (imx208->identified)
63256ca3be8SBingbu Cao return 0;
63356ca3be8SBingbu Cao
63456ca3be8SBingbu Cao ret = imx208_read_reg(imx208, IMX208_REG_CHIP_ID,
63556ca3be8SBingbu Cao 2, &val);
63656ca3be8SBingbu Cao if (ret) {
63756ca3be8SBingbu Cao dev_err(&client->dev, "failed to read chip id %x\n",
63856ca3be8SBingbu Cao IMX208_CHIP_ID);
63956ca3be8SBingbu Cao return ret;
64056ca3be8SBingbu Cao }
64156ca3be8SBingbu Cao
64256ca3be8SBingbu Cao if (val != IMX208_CHIP_ID) {
64356ca3be8SBingbu Cao dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
64456ca3be8SBingbu Cao IMX208_CHIP_ID, val);
64556ca3be8SBingbu Cao return -EIO;
64656ca3be8SBingbu Cao }
64756ca3be8SBingbu Cao
64856ca3be8SBingbu Cao imx208->identified = true;
64956ca3be8SBingbu Cao
65056ca3be8SBingbu Cao return 0;
65156ca3be8SBingbu Cao }
65256ca3be8SBingbu Cao
653d953e3cbSShawn Tu /* Start streaming */
imx208_start_streaming(struct imx208 * imx208)654d953e3cbSShawn Tu static int imx208_start_streaming(struct imx208 *imx208)
655d953e3cbSShawn Tu {
656d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
657d953e3cbSShawn Tu const struct imx208_reg_list *reg_list;
658d953e3cbSShawn Tu int ret, link_freq_index;
659d953e3cbSShawn Tu
66056ca3be8SBingbu Cao ret = imx208_identify_module(imx208);
66156ca3be8SBingbu Cao if (ret)
66256ca3be8SBingbu Cao return ret;
66356ca3be8SBingbu Cao
664d953e3cbSShawn Tu /* Setup PLL */
665d953e3cbSShawn Tu link_freq_index = imx208->cur_mode->link_freq_index;
666d953e3cbSShawn Tu reg_list = &link_freq_configs[link_freq_index].reg_list;
667d953e3cbSShawn Tu ret = imx208_write_regs(imx208, reg_list->regs, reg_list->num_of_regs);
668d953e3cbSShawn Tu if (ret) {
669d953e3cbSShawn Tu dev_err(&client->dev, "%s failed to set plls\n", __func__);
670d953e3cbSShawn Tu return ret;
671d953e3cbSShawn Tu }
672d953e3cbSShawn Tu
673d953e3cbSShawn Tu /* Apply default values of current mode */
674d953e3cbSShawn Tu reg_list = &imx208->cur_mode->reg_list;
675d953e3cbSShawn Tu ret = imx208_write_regs(imx208, reg_list->regs, reg_list->num_of_regs);
676d953e3cbSShawn Tu if (ret) {
677d953e3cbSShawn Tu dev_err(&client->dev, "%s failed to set mode\n", __func__);
678d953e3cbSShawn Tu return ret;
679d953e3cbSShawn Tu }
680d953e3cbSShawn Tu
681d953e3cbSShawn Tu /* Apply customized values from user */
682d953e3cbSShawn Tu ret = __v4l2_ctrl_handler_setup(imx208->sd.ctrl_handler);
683d953e3cbSShawn Tu if (ret)
684d953e3cbSShawn Tu return ret;
685d953e3cbSShawn Tu
686d953e3cbSShawn Tu /* set stream on register */
687d953e3cbSShawn Tu return imx208_write_reg(imx208, IMX208_REG_MODE_SELECT,
688d953e3cbSShawn Tu 1, IMX208_MODE_STREAMING);
689d953e3cbSShawn Tu }
690d953e3cbSShawn Tu
691d953e3cbSShawn Tu /* Stop streaming */
imx208_stop_streaming(struct imx208 * imx208)692d953e3cbSShawn Tu static int imx208_stop_streaming(struct imx208 *imx208)
693d953e3cbSShawn Tu {
694d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
695d953e3cbSShawn Tu int ret;
696d953e3cbSShawn Tu
697d953e3cbSShawn Tu /* set stream off register */
698d953e3cbSShawn Tu ret = imx208_write_reg(imx208, IMX208_REG_MODE_SELECT,
699d953e3cbSShawn Tu 1, IMX208_MODE_STANDBY);
700d953e3cbSShawn Tu if (ret)
701d953e3cbSShawn Tu dev_err(&client->dev, "%s failed to set stream\n", __func__);
702d953e3cbSShawn Tu
703d953e3cbSShawn Tu /*
704d953e3cbSShawn Tu * Return success even if it was an error, as there is nothing the
705d953e3cbSShawn Tu * caller can do about it.
706d953e3cbSShawn Tu */
707d953e3cbSShawn Tu return 0;
708d953e3cbSShawn Tu }
709d953e3cbSShawn Tu
imx208_set_stream(struct v4l2_subdev * sd,int enable)710d953e3cbSShawn Tu static int imx208_set_stream(struct v4l2_subdev *sd, int enable)
711d953e3cbSShawn Tu {
712d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
713d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(sd);
714d953e3cbSShawn Tu int ret = 0;
715d953e3cbSShawn Tu
716d953e3cbSShawn Tu mutex_lock(&imx208->imx208_mx);
717d953e3cbSShawn Tu if (imx208->streaming == enable) {
718d953e3cbSShawn Tu mutex_unlock(&imx208->imx208_mx);
719d953e3cbSShawn Tu return 0;
720d953e3cbSShawn Tu }
721d953e3cbSShawn Tu
722d953e3cbSShawn Tu if (enable) {
723d953e3cbSShawn Tu ret = pm_runtime_get_sync(&client->dev);
724d953e3cbSShawn Tu if (ret < 0)
725d953e3cbSShawn Tu goto err_rpm_put;
726d953e3cbSShawn Tu
727d953e3cbSShawn Tu /*
728d953e3cbSShawn Tu * Apply default & customized values
729d953e3cbSShawn Tu * and then start streaming.
730d953e3cbSShawn Tu */
731d953e3cbSShawn Tu ret = imx208_start_streaming(imx208);
732d953e3cbSShawn Tu if (ret)
733d953e3cbSShawn Tu goto err_rpm_put;
734d953e3cbSShawn Tu } else {
735d953e3cbSShawn Tu imx208_stop_streaming(imx208);
736d953e3cbSShawn Tu pm_runtime_put(&client->dev);
737d953e3cbSShawn Tu }
738d953e3cbSShawn Tu
739d953e3cbSShawn Tu imx208->streaming = enable;
740d953e3cbSShawn Tu mutex_unlock(&imx208->imx208_mx);
741d953e3cbSShawn Tu
742d953e3cbSShawn Tu /* vflip and hflip cannot change during streaming */
743d953e3cbSShawn Tu v4l2_ctrl_grab(imx208->vflip, enable);
744d953e3cbSShawn Tu v4l2_ctrl_grab(imx208->hflip, enable);
745d953e3cbSShawn Tu
746d953e3cbSShawn Tu return ret;
747d953e3cbSShawn Tu
748d953e3cbSShawn Tu err_rpm_put:
749d953e3cbSShawn Tu pm_runtime_put(&client->dev);
750d953e3cbSShawn Tu mutex_unlock(&imx208->imx208_mx);
751d953e3cbSShawn Tu
752d953e3cbSShawn Tu return ret;
753d953e3cbSShawn Tu }
754d953e3cbSShawn Tu
imx208_suspend(struct device * dev)755d953e3cbSShawn Tu static int __maybe_unused imx208_suspend(struct device *dev)
756d953e3cbSShawn Tu {
757d953e3cbSShawn Tu struct i2c_client *client = to_i2c_client(dev);
758d953e3cbSShawn Tu struct v4l2_subdev *sd = i2c_get_clientdata(client);
759d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
760d953e3cbSShawn Tu
761d953e3cbSShawn Tu if (imx208->streaming)
762d953e3cbSShawn Tu imx208_stop_streaming(imx208);
763d953e3cbSShawn Tu
764d953e3cbSShawn Tu return 0;
765d953e3cbSShawn Tu }
766d953e3cbSShawn Tu
imx208_resume(struct device * dev)767d953e3cbSShawn Tu static int __maybe_unused imx208_resume(struct device *dev)
768d953e3cbSShawn Tu {
769d953e3cbSShawn Tu struct i2c_client *client = to_i2c_client(dev);
770d953e3cbSShawn Tu struct v4l2_subdev *sd = i2c_get_clientdata(client);
771d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
772d953e3cbSShawn Tu int ret;
773d953e3cbSShawn Tu
774d953e3cbSShawn Tu if (imx208->streaming) {
775d953e3cbSShawn Tu ret = imx208_start_streaming(imx208);
776d953e3cbSShawn Tu if (ret)
777d953e3cbSShawn Tu goto error;
778d953e3cbSShawn Tu }
779d953e3cbSShawn Tu
780d953e3cbSShawn Tu return 0;
781d953e3cbSShawn Tu
782d953e3cbSShawn Tu error:
783d953e3cbSShawn Tu imx208_stop_streaming(imx208);
784d953e3cbSShawn Tu imx208->streaming = 0;
785d953e3cbSShawn Tu
786d953e3cbSShawn Tu return ret;
787d953e3cbSShawn Tu }
788d953e3cbSShawn Tu
789d953e3cbSShawn Tu /* Verify chip ID */
790d953e3cbSShawn Tu static const struct v4l2_subdev_video_ops imx208_video_ops = {
791d953e3cbSShawn Tu .s_stream = imx208_set_stream,
792d953e3cbSShawn Tu };
793d953e3cbSShawn Tu
794d953e3cbSShawn Tu static const struct v4l2_subdev_pad_ops imx208_pad_ops = {
795d953e3cbSShawn Tu .enum_mbus_code = imx208_enum_mbus_code,
796d953e3cbSShawn Tu .get_fmt = imx208_get_pad_format,
797d953e3cbSShawn Tu .set_fmt = imx208_set_pad_format,
798d953e3cbSShawn Tu .enum_frame_size = imx208_enum_frame_size,
799d953e3cbSShawn Tu };
800d953e3cbSShawn Tu
801d953e3cbSShawn Tu static const struct v4l2_subdev_ops imx208_subdev_ops = {
802d953e3cbSShawn Tu .video = &imx208_video_ops,
803d953e3cbSShawn Tu .pad = &imx208_pad_ops,
804d953e3cbSShawn Tu };
805d953e3cbSShawn Tu
806d953e3cbSShawn Tu static const struct v4l2_subdev_internal_ops imx208_internal_ops = {
807d953e3cbSShawn Tu .open = imx208_open,
808d953e3cbSShawn Tu };
809d953e3cbSShawn Tu
imx208_read_otp(struct imx208 * imx208)810d953e3cbSShawn Tu static int imx208_read_otp(struct imx208 *imx208)
811d953e3cbSShawn Tu {
812d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
813d953e3cbSShawn Tu struct i2c_msg msgs[2];
814d953e3cbSShawn Tu u8 addr_buf[2] = { IMX208_OTP_BASE >> 8, IMX208_OTP_BASE & 0xff };
815d953e3cbSShawn Tu int ret = 0;
816d953e3cbSShawn Tu
817d953e3cbSShawn Tu mutex_lock(&imx208->imx208_mx);
818d953e3cbSShawn Tu
819d953e3cbSShawn Tu if (imx208->otp_read)
820d953e3cbSShawn Tu goto out_unlock;
821d953e3cbSShawn Tu
822d953e3cbSShawn Tu ret = pm_runtime_get_sync(&client->dev);
823d953e3cbSShawn Tu if (ret < 0) {
824d953e3cbSShawn Tu pm_runtime_put_noidle(&client->dev);
825d953e3cbSShawn Tu goto out_unlock;
826d953e3cbSShawn Tu }
827d953e3cbSShawn Tu
82856ca3be8SBingbu Cao ret = imx208_identify_module(imx208);
82956ca3be8SBingbu Cao if (ret)
83056ca3be8SBingbu Cao goto out_pm_put;
83156ca3be8SBingbu Cao
832d953e3cbSShawn Tu /* Write register address */
833d953e3cbSShawn Tu msgs[0].addr = client->addr;
834d953e3cbSShawn Tu msgs[0].flags = 0;
835d953e3cbSShawn Tu msgs[0].len = ARRAY_SIZE(addr_buf);
836d953e3cbSShawn Tu msgs[0].buf = addr_buf;
837d953e3cbSShawn Tu
838d953e3cbSShawn Tu /* Read data from registers */
839d953e3cbSShawn Tu msgs[1].addr = client->addr;
840d953e3cbSShawn Tu msgs[1].flags = I2C_M_RD;
841d953e3cbSShawn Tu msgs[1].len = sizeof(imx208->otp_data);
842d953e3cbSShawn Tu msgs[1].buf = imx208->otp_data;
843d953e3cbSShawn Tu
844d953e3cbSShawn Tu ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
845d953e3cbSShawn Tu if (ret == ARRAY_SIZE(msgs)) {
846d953e3cbSShawn Tu imx208->otp_read = true;
847d953e3cbSShawn Tu ret = 0;
848d953e3cbSShawn Tu }
849d953e3cbSShawn Tu
85056ca3be8SBingbu Cao out_pm_put:
851d953e3cbSShawn Tu pm_runtime_put(&client->dev);
852d953e3cbSShawn Tu
853d953e3cbSShawn Tu out_unlock:
854d953e3cbSShawn Tu mutex_unlock(&imx208->imx208_mx);
855d953e3cbSShawn Tu
856d953e3cbSShawn Tu return ret;
857d953e3cbSShawn Tu }
858d953e3cbSShawn Tu
otp_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)859d953e3cbSShawn Tu static ssize_t otp_read(struct file *filp, struct kobject *kobj,
860d953e3cbSShawn Tu struct bin_attribute *bin_attr,
861d953e3cbSShawn Tu char *buf, loff_t off, size_t count)
862d953e3cbSShawn Tu {
863d953e3cbSShawn Tu struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
864d953e3cbSShawn Tu struct v4l2_subdev *sd = i2c_get_clientdata(client);
865d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
866d953e3cbSShawn Tu int ret;
867d953e3cbSShawn Tu
868d953e3cbSShawn Tu ret = imx208_read_otp(imx208);
869d953e3cbSShawn Tu if (ret)
870d953e3cbSShawn Tu return ret;
871d953e3cbSShawn Tu
872d953e3cbSShawn Tu memcpy(buf, &imx208->otp_data[off], count);
873d953e3cbSShawn Tu return count;
874d953e3cbSShawn Tu }
875d953e3cbSShawn Tu
876d953e3cbSShawn Tu static const BIN_ATTR_RO(otp, IMX208_OTP_SIZE);
877d953e3cbSShawn Tu
878d953e3cbSShawn Tu /* Initialize control handlers */
imx208_init_controls(struct imx208 * imx208)879d953e3cbSShawn Tu static int imx208_init_controls(struct imx208 *imx208)
880d953e3cbSShawn Tu {
881d953e3cbSShawn Tu struct i2c_client *client = v4l2_get_subdevdata(&imx208->sd);
882d953e3cbSShawn Tu struct v4l2_ctrl_handler *ctrl_hdlr = &imx208->ctrl_handler;
883d953e3cbSShawn Tu s64 exposure_max;
884d953e3cbSShawn Tu s64 vblank_def;
885d953e3cbSShawn Tu s64 vblank_min;
886d953e3cbSShawn Tu s64 pixel_rate_min;
887d953e3cbSShawn Tu s64 pixel_rate_max;
888d953e3cbSShawn Tu int ret;
889d953e3cbSShawn Tu
890d953e3cbSShawn Tu ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
891d953e3cbSShawn Tu if (ret)
892d953e3cbSShawn Tu return ret;
893d953e3cbSShawn Tu
894d953e3cbSShawn Tu mutex_init(&imx208->imx208_mx);
895d953e3cbSShawn Tu ctrl_hdlr->lock = &imx208->imx208_mx;
896d953e3cbSShawn Tu imx208->link_freq =
897d953e3cbSShawn Tu v4l2_ctrl_new_int_menu(ctrl_hdlr,
898d953e3cbSShawn Tu &imx208_ctrl_ops,
899d953e3cbSShawn Tu V4L2_CID_LINK_FREQ,
900d953e3cbSShawn Tu ARRAY_SIZE(link_freq_menu_items) - 1,
901d953e3cbSShawn Tu 0, link_freq_menu_items);
902d953e3cbSShawn Tu
903d953e3cbSShawn Tu if (imx208->link_freq)
904d953e3cbSShawn Tu imx208->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
905d953e3cbSShawn Tu
906d953e3cbSShawn Tu pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]);
907d953e3cbSShawn Tu pixel_rate_min =
908d953e3cbSShawn Tu link_freq_to_pixel_rate(link_freq_menu_items[ARRAY_SIZE(link_freq_menu_items) - 1]);
909d953e3cbSShawn Tu /* By default, PIXEL_RATE is read only */
910d953e3cbSShawn Tu imx208->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops,
911d953e3cbSShawn Tu V4L2_CID_PIXEL_RATE,
912d953e3cbSShawn Tu pixel_rate_min, pixel_rate_max,
913d953e3cbSShawn Tu 1, pixel_rate_max);
914d953e3cbSShawn Tu
915d953e3cbSShawn Tu vblank_def = imx208->cur_mode->vts_def - imx208->cur_mode->height;
916d953e3cbSShawn Tu vblank_min = imx208->cur_mode->vts_min - imx208->cur_mode->height;
917d953e3cbSShawn Tu imx208->vblank =
918d953e3cbSShawn Tu v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops, V4L2_CID_VBLANK,
919d953e3cbSShawn Tu vblank_min,
920d953e3cbSShawn Tu IMX208_VTS_MAX - imx208->cur_mode->height, 1,
921d953e3cbSShawn Tu vblank_def);
922d953e3cbSShawn Tu
923d953e3cbSShawn Tu imx208->hblank =
924d953e3cbSShawn Tu v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops, V4L2_CID_HBLANK,
925d953e3cbSShawn Tu IMX208_PPL_384MHZ - imx208->cur_mode->width,
926d953e3cbSShawn Tu IMX208_PPL_384MHZ - imx208->cur_mode->width,
927d953e3cbSShawn Tu 1,
928d953e3cbSShawn Tu IMX208_PPL_384MHZ - imx208->cur_mode->width);
929d953e3cbSShawn Tu
930d953e3cbSShawn Tu if (imx208->hblank)
931d953e3cbSShawn Tu imx208->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
932d953e3cbSShawn Tu
933d953e3cbSShawn Tu exposure_max = imx208->cur_mode->vts_def - 8;
934d953e3cbSShawn Tu v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops, V4L2_CID_EXPOSURE,
935d953e3cbSShawn Tu IMX208_EXPOSURE_MIN, exposure_max,
936d953e3cbSShawn Tu IMX208_EXPOSURE_STEP, IMX208_EXPOSURE_DEFAULT);
937d953e3cbSShawn Tu
938d953e3cbSShawn Tu imx208->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops,
939d953e3cbSShawn Tu V4L2_CID_HFLIP, 0, 1, 1, 0);
940f19ba70fSDave Stevenson if (imx208->hflip)
941f19ba70fSDave Stevenson imx208->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
942d953e3cbSShawn Tu imx208->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops,
943d953e3cbSShawn Tu V4L2_CID_VFLIP, 0, 1, 1, 0);
944f19ba70fSDave Stevenson if (imx208->vflip)
945f19ba70fSDave Stevenson imx208->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
946d953e3cbSShawn Tu
947d953e3cbSShawn Tu v4l2_ctrl_new_std(ctrl_hdlr, &imx208_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
948d953e3cbSShawn Tu IMX208_ANA_GAIN_MIN, IMX208_ANA_GAIN_MAX,
949d953e3cbSShawn Tu IMX208_ANA_GAIN_STEP, IMX208_ANA_GAIN_DEFAULT);
950d953e3cbSShawn Tu
951d953e3cbSShawn Tu v4l2_ctrl_new_custom(ctrl_hdlr, &imx208_digital_gain_control, NULL);
952d953e3cbSShawn Tu
953d953e3cbSShawn Tu v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx208_ctrl_ops,
954d953e3cbSShawn Tu V4L2_CID_TEST_PATTERN,
955d953e3cbSShawn Tu ARRAY_SIZE(imx208_test_pattern_menu) - 1,
956d953e3cbSShawn Tu 0, 0, imx208_test_pattern_menu);
957d953e3cbSShawn Tu
958d953e3cbSShawn Tu if (ctrl_hdlr->error) {
959d953e3cbSShawn Tu ret = ctrl_hdlr->error;
960d953e3cbSShawn Tu dev_err(&client->dev, "%s control init failed (%d)\n",
961d953e3cbSShawn Tu __func__, ret);
962d953e3cbSShawn Tu goto error;
963d953e3cbSShawn Tu }
964d953e3cbSShawn Tu
965d953e3cbSShawn Tu imx208->sd.ctrl_handler = ctrl_hdlr;
966d953e3cbSShawn Tu
967d953e3cbSShawn Tu return 0;
968d953e3cbSShawn Tu
969d953e3cbSShawn Tu error:
970d953e3cbSShawn Tu v4l2_ctrl_handler_free(ctrl_hdlr);
971d953e3cbSShawn Tu mutex_destroy(&imx208->imx208_mx);
972d953e3cbSShawn Tu
973d953e3cbSShawn Tu return ret;
974d953e3cbSShawn Tu }
975d953e3cbSShawn Tu
imx208_free_controls(struct imx208 * imx208)976d953e3cbSShawn Tu static void imx208_free_controls(struct imx208 *imx208)
977d953e3cbSShawn Tu {
978d953e3cbSShawn Tu v4l2_ctrl_handler_free(imx208->sd.ctrl_handler);
979d953e3cbSShawn Tu }
980d953e3cbSShawn Tu
imx208_probe(struct i2c_client * client)981d953e3cbSShawn Tu static int imx208_probe(struct i2c_client *client)
982d953e3cbSShawn Tu {
983d953e3cbSShawn Tu struct imx208 *imx208;
984d953e3cbSShawn Tu int ret;
98556ca3be8SBingbu Cao bool full_power;
986d953e3cbSShawn Tu u32 val = 0;
987d953e3cbSShawn Tu
988d953e3cbSShawn Tu device_property_read_u32(&client->dev, "clock-frequency", &val);
989d953e3cbSShawn Tu if (val != 19200000) {
990d953e3cbSShawn Tu dev_err(&client->dev,
991d953e3cbSShawn Tu "Unsupported clock-frequency %u. Expected 19200000.\n",
992d953e3cbSShawn Tu val);
993d953e3cbSShawn Tu return -EINVAL;
994d953e3cbSShawn Tu }
995d953e3cbSShawn Tu
996d953e3cbSShawn Tu imx208 = devm_kzalloc(&client->dev, sizeof(*imx208), GFP_KERNEL);
997d953e3cbSShawn Tu if (!imx208)
998d953e3cbSShawn Tu return -ENOMEM;
999d953e3cbSShawn Tu
1000d953e3cbSShawn Tu /* Initialize subdev */
1001d953e3cbSShawn Tu v4l2_i2c_subdev_init(&imx208->sd, client, &imx208_subdev_ops);
1002d953e3cbSShawn Tu
100356ca3be8SBingbu Cao full_power = acpi_dev_state_d0(&client->dev);
100456ca3be8SBingbu Cao if (full_power) {
1005d953e3cbSShawn Tu /* Check module identity */
1006d953e3cbSShawn Tu ret = imx208_identify_module(imx208);
1007d953e3cbSShawn Tu if (ret) {
1008d953e3cbSShawn Tu dev_err(&client->dev, "failed to find sensor: %d", ret);
1009d953e3cbSShawn Tu goto error_probe;
1010d953e3cbSShawn Tu }
101156ca3be8SBingbu Cao }
1012d953e3cbSShawn Tu
1013d953e3cbSShawn Tu /* Set default mode to max resolution */
1014d953e3cbSShawn Tu imx208->cur_mode = &supported_modes[0];
1015d953e3cbSShawn Tu
1016d953e3cbSShawn Tu ret = imx208_init_controls(imx208);
1017d953e3cbSShawn Tu if (ret) {
1018d953e3cbSShawn Tu dev_err(&client->dev, "failed to init controls: %d", ret);
1019d953e3cbSShawn Tu goto error_probe;
1020d953e3cbSShawn Tu }
1021d953e3cbSShawn Tu
1022d953e3cbSShawn Tu /* Initialize subdev */
1023d953e3cbSShawn Tu imx208->sd.internal_ops = &imx208_internal_ops;
1024d953e3cbSShawn Tu imx208->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1025d953e3cbSShawn Tu imx208->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1026d953e3cbSShawn Tu
1027d953e3cbSShawn Tu /* Initialize source pad */
1028d953e3cbSShawn Tu imx208->pad.flags = MEDIA_PAD_FL_SOURCE;
1029d953e3cbSShawn Tu ret = media_entity_pads_init(&imx208->sd.entity, 1, &imx208->pad);
1030d953e3cbSShawn Tu if (ret) {
1031d953e3cbSShawn Tu dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
1032d953e3cbSShawn Tu goto error_handler_free;
1033d953e3cbSShawn Tu }
1034d953e3cbSShawn Tu
1035d953e3cbSShawn Tu ret = v4l2_async_register_subdev_sensor(&imx208->sd);
1036d953e3cbSShawn Tu if (ret < 0)
1037d953e3cbSShawn Tu goto error_media_entity;
1038d953e3cbSShawn Tu
1039d953e3cbSShawn Tu ret = device_create_bin_file(&client->dev, &bin_attr_otp);
1040d953e3cbSShawn Tu if (ret) {
1041d953e3cbSShawn Tu dev_err(&client->dev, "sysfs otp creation failed\n");
1042d953e3cbSShawn Tu goto error_async_subdev;
1043d953e3cbSShawn Tu }
1044d953e3cbSShawn Tu
104556ca3be8SBingbu Cao /* Set the device's state to active if it's in D0 state. */
104656ca3be8SBingbu Cao if (full_power)
1047d953e3cbSShawn Tu pm_runtime_set_active(&client->dev);
1048d953e3cbSShawn Tu pm_runtime_enable(&client->dev);
1049d953e3cbSShawn Tu pm_runtime_idle(&client->dev);
1050d953e3cbSShawn Tu
1051d953e3cbSShawn Tu return 0;
1052d953e3cbSShawn Tu
1053d953e3cbSShawn Tu error_async_subdev:
1054d953e3cbSShawn Tu v4l2_async_unregister_subdev(&imx208->sd);
1055d953e3cbSShawn Tu
1056d953e3cbSShawn Tu error_media_entity:
1057d953e3cbSShawn Tu media_entity_cleanup(&imx208->sd.entity);
1058d953e3cbSShawn Tu
1059d953e3cbSShawn Tu error_handler_free:
1060d953e3cbSShawn Tu imx208_free_controls(imx208);
1061d953e3cbSShawn Tu
1062d953e3cbSShawn Tu error_probe:
1063d953e3cbSShawn Tu mutex_destroy(&imx208->imx208_mx);
1064d953e3cbSShawn Tu
1065d953e3cbSShawn Tu return ret;
1066d953e3cbSShawn Tu }
1067d953e3cbSShawn Tu
imx208_remove(struct i2c_client * client)1068ed5c2f5fSUwe Kleine-König static void imx208_remove(struct i2c_client *client)
1069d953e3cbSShawn Tu {
1070d953e3cbSShawn Tu struct v4l2_subdev *sd = i2c_get_clientdata(client);
1071d953e3cbSShawn Tu struct imx208 *imx208 = to_imx208(sd);
1072d953e3cbSShawn Tu
1073d953e3cbSShawn Tu device_remove_bin_file(&client->dev, &bin_attr_otp);
1074d953e3cbSShawn Tu v4l2_async_unregister_subdev(sd);
1075d953e3cbSShawn Tu media_entity_cleanup(&sd->entity);
1076d953e3cbSShawn Tu imx208_free_controls(imx208);
1077d953e3cbSShawn Tu
1078d953e3cbSShawn Tu pm_runtime_disable(&client->dev);
1079d953e3cbSShawn Tu pm_runtime_set_suspended(&client->dev);
1080d953e3cbSShawn Tu
1081d953e3cbSShawn Tu mutex_destroy(&imx208->imx208_mx);
1082d953e3cbSShawn Tu }
1083d953e3cbSShawn Tu
1084d953e3cbSShawn Tu static const struct dev_pm_ops imx208_pm_ops = {
1085d953e3cbSShawn Tu SET_SYSTEM_SLEEP_PM_OPS(imx208_suspend, imx208_resume)
1086d953e3cbSShawn Tu };
1087d953e3cbSShawn Tu
1088d953e3cbSShawn Tu #ifdef CONFIG_ACPI
1089d953e3cbSShawn Tu static const struct acpi_device_id imx208_acpi_ids[] = {
1090d953e3cbSShawn Tu { "INT3478" },
1091d953e3cbSShawn Tu { /* sentinel */ }
1092d953e3cbSShawn Tu };
1093d953e3cbSShawn Tu
1094d953e3cbSShawn Tu MODULE_DEVICE_TABLE(acpi, imx208_acpi_ids);
1095d953e3cbSShawn Tu #endif
1096d953e3cbSShawn Tu
1097d953e3cbSShawn Tu static struct i2c_driver imx208_i2c_driver = {
1098d953e3cbSShawn Tu .driver = {
1099d953e3cbSShawn Tu .name = "imx208",
1100d953e3cbSShawn Tu .pm = &imx208_pm_ops,
1101d953e3cbSShawn Tu .acpi_match_table = ACPI_PTR(imx208_acpi_ids),
1102d953e3cbSShawn Tu },
1103aaeb31c0SUwe Kleine-König .probe = imx208_probe,
1104d953e3cbSShawn Tu .remove = imx208_remove,
110556ca3be8SBingbu Cao .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE,
1106d953e3cbSShawn Tu };
1107d953e3cbSShawn Tu
1108d953e3cbSShawn Tu module_i2c_driver(imx208_i2c_driver);
1109d953e3cbSShawn Tu
1110d953e3cbSShawn Tu MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>");
1111d953e3cbSShawn Tu MODULE_AUTHOR("Chen, Ping-chung <ping-chung.chen@intel.com>");
1112*4106cd72SSakari Ailus MODULE_AUTHOR("Shawn Tu");
1113d953e3cbSShawn Tu MODULE_DESCRIPTION("Sony IMX208 sensor driver");
1114d953e3cbSShawn Tu MODULE_LICENSE("GPL v2");
1115