blob: 58d134dcdf446075cc8dfcf081cc7bcad247c62c [file] [log] [blame]
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001/*
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -03002 * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
Robert Jarzmik77110ab2008-08-14 12:02:51 -03003 *
4 * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/videodev2.h>
11#include <linux/slab.h>
12#include <linux/i2c.h>
13#include <linux/log2.h>
14#include <linux/gpio.h>
15#include <linux/delay.h>
Guennadi Liakhovetski95d20102011-09-09 13:56:04 -030016#include <linux/v4l2-mediabus.h>
Paul Gortmaker7a707b82011-07-03 14:03:12 -040017#include <linux/module.h>
Robert Jarzmik77110ab2008-08-14 12:02:51 -030018
Robert Jarzmik5d7cc012016-09-06 06:04:11 -030019#include <media/v4l2-async.h>
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -030020#include <media/v4l2-clk.h>
Robert Jarzmik77110ab2008-08-14 12:02:51 -030021#include <media/v4l2-common.h>
Hans Verkuilaf8425c2011-09-07 06:56:57 -030022#include <media/v4l2-ctrls.h>
Robert Jarzmik5d7cc012016-09-06 06:04:11 -030023#include <media/v4l2-device.h>
Akinobu Mita329d9e352018-11-12 11:00:48 -050024#include <media/v4l2-event.h>
Robert Jarzmik77110ab2008-08-14 12:02:51 -030025
26/*
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -030027 * MT9M111, MT9M112 and MT9M131:
28 * i2c address is 0x48 or 0x5d (depending on SADDR pin)
Guennadi Liakhovetski25a34812012-12-21 08:11:48 -030029 * The platform has to define struct i2c_board_info objects and link to them
30 * from struct soc_camera_host_desc
Robert Jarzmik77110ab2008-08-14 12:02:51 -030031 */
32
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -030033/*
34 * Sensor core register addresses (0x000..0x0ff)
35 */
Robert Jarzmik77110ab2008-08-14 12:02:51 -030036#define MT9M111_CHIP_VERSION 0x000
37#define MT9M111_ROW_START 0x001
38#define MT9M111_COLUMN_START 0x002
39#define MT9M111_WINDOW_HEIGHT 0x003
40#define MT9M111_WINDOW_WIDTH 0x004
41#define MT9M111_HORIZONTAL_BLANKING_B 0x005
42#define MT9M111_VERTICAL_BLANKING_B 0x006
43#define MT9M111_HORIZONTAL_BLANKING_A 0x007
44#define MT9M111_VERTICAL_BLANKING_A 0x008
45#define MT9M111_SHUTTER_WIDTH 0x009
46#define MT9M111_ROW_SPEED 0x00a
47#define MT9M111_EXTRA_DELAY 0x00b
48#define MT9M111_SHUTTER_DELAY 0x00c
49#define MT9M111_RESET 0x00d
50#define MT9M111_READ_MODE_B 0x020
51#define MT9M111_READ_MODE_A 0x021
52#define MT9M111_FLASH_CONTROL 0x023
53#define MT9M111_GREEN1_GAIN 0x02b
54#define MT9M111_BLUE_GAIN 0x02c
55#define MT9M111_RED_GAIN 0x02d
56#define MT9M111_GREEN2_GAIN 0x02e
57#define MT9M111_GLOBAL_GAIN 0x02f
58#define MT9M111_CONTEXT_CONTROL 0x0c8
59#define MT9M111_PAGE_MAP 0x0f0
60#define MT9M111_BYTE_WISE_ADDR 0x0f1
61
62#define MT9M111_RESET_SYNC_CHANGES (1 << 15)
63#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9)
64#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8)
65#define MT9M111_RESET_RESET_SOC (1 << 5)
66#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4)
67#define MT9M111_RESET_CHIP_ENABLE (1 << 3)
68#define MT9M111_RESET_ANALOG_STANDBY (1 << 2)
69#define MT9M111_RESET_RESTART_FRAME (1 << 1)
70#define MT9M111_RESET_RESET_MODE (1 << 0)
71
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -030072#define MT9M111_RM_FULL_POWER_RD (0 << 10)
73#define MT9M111_RM_LOW_POWER_RD (1 << 10)
74#define MT9M111_RM_COL_SKIP_4X (1 << 5)
75#define MT9M111_RM_ROW_SKIP_4X (1 << 4)
76#define MT9M111_RM_COL_SKIP_2X (1 << 3)
77#define MT9M111_RM_ROW_SKIP_2X (1 << 2)
Robert Jarzmik77110ab2008-08-14 12:02:51 -030078#define MT9M111_RMB_MIRROR_COLS (1 << 1)
79#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
80#define MT9M111_CTXT_CTRL_RESTART (1 << 15)
81#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12)
82#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10)
83#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9)
84#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8)
85#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7)
86#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3)
87#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
88#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
89#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -030090
Robert Jarzmik77110ab2008-08-14 12:02:51 -030091/*
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -030092 * Colorpipe register addresses (0x100..0x1ff)
Robert Jarzmik77110ab2008-08-14 12:02:51 -030093 */
94#define MT9M111_OPER_MODE_CTRL 0x106
95#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
Akinobu Mita74e08732018-01-03 13:22:47 -050096#define MT9M111_TPG_CTRL 0x148
Robert Jarzmik77110ab2008-08-14 12:02:51 -030097#define MT9M111_REDUCER_XZOOM_B 0x1a0
98#define MT9M111_REDUCER_XSIZE_B 0x1a1
99#define MT9M111_REDUCER_YZOOM_B 0x1a3
100#define MT9M111_REDUCER_YSIZE_B 0x1a4
101#define MT9M111_REDUCER_XZOOM_A 0x1a6
102#define MT9M111_REDUCER_XSIZE_A 0x1a7
103#define MT9M111_REDUCER_YZOOM_A 0x1a9
104#define MT9M111_REDUCER_YSIZE_A 0x1aa
Akinobu Mitadde64f72018-11-12 11:00:49 -0500105#define MT9M111_EFFECTS_MODE 0x1e2
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300106
107#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a
108#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
109
110#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
Robert Jarzmik39bf3722008-12-18 11:29:05 -0300111#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300112#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9)
113#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300114#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
115#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
116#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9)
117#define MT9M111_OUTFMT_RGB (1 << 8)
Michael Grzeschikec733652010-08-03 07:57:41 -0300118#define MT9M111_OUTFMT_RGB565 (0 << 6)
119#define MT9M111_OUTFMT_RGB555 (1 << 6)
120#define MT9M111_OUTFMT_RGB444x (2 << 6)
121#define MT9M111_OUTFMT_RGBx444 (3 << 6)
122#define MT9M111_OUTFMT_TST_RAMP_OFF (0 << 4)
123#define MT9M111_OUTFMT_TST_RAMP_COL (1 << 4)
124#define MT9M111_OUTFMT_TST_RAMP_ROW (2 << 4)
125#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300126#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3)
127#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2)
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300128#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
129#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0)
Akinobu Mita74e08732018-01-03 13:22:47 -0500130#define MT9M111_TPG_SEL_MASK GENMASK(2, 0)
Akinobu Mitadde64f72018-11-12 11:00:49 -0500131#define MT9M111_EFFECTS_MODE_MASK GENMASK(2, 0)
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -0300132
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300133/*
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -0300134 * Camera control register addresses (0x200..0x2ff not implemented)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300135 */
136
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300137#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
138#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
139#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
140#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300141#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
142 (val), (mask))
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300143
144#define MT9M111_MIN_DARK_ROWS 8
Michael Grzeschik669470a2010-08-03 07:57:43 -0300145#define MT9M111_MIN_DARK_COLS 26
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300146#define MT9M111_MAX_HEIGHT 1024
147#define MT9M111_MAX_WIDTH 1280
148
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300149struct mt9m111_context {
150 u16 read_mode;
151 u16 blanking_h;
152 u16 blanking_v;
153 u16 reducer_xzoom;
154 u16 reducer_yzoom;
155 u16 reducer_xsize;
156 u16 reducer_ysize;
157 u16 output_fmt_ctrl2;
158 u16 control;
159};
160
161static struct mt9m111_context context_a = {
162 .read_mode = MT9M111_READ_MODE_A,
163 .blanking_h = MT9M111_HORIZONTAL_BLANKING_A,
164 .blanking_v = MT9M111_VERTICAL_BLANKING_A,
165 .reducer_xzoom = MT9M111_REDUCER_XZOOM_A,
166 .reducer_yzoom = MT9M111_REDUCER_YZOOM_A,
167 .reducer_xsize = MT9M111_REDUCER_XSIZE_A,
168 .reducer_ysize = MT9M111_REDUCER_YSIZE_A,
169 .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_A,
170 .control = MT9M111_CTXT_CTRL_RESTART,
171};
172
173static struct mt9m111_context context_b = {
174 .read_mode = MT9M111_READ_MODE_B,
175 .blanking_h = MT9M111_HORIZONTAL_BLANKING_B,
176 .blanking_v = MT9M111_VERTICAL_BLANKING_B,
177 .reducer_xzoom = MT9M111_REDUCER_XZOOM_B,
178 .reducer_yzoom = MT9M111_REDUCER_YZOOM_B,
179 .reducer_xsize = MT9M111_REDUCER_XSIZE_B,
180 .reducer_ysize = MT9M111_REDUCER_YSIZE_B,
181 .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_B,
182 .control = MT9M111_CTXT_CTRL_RESTART |
183 MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B |
184 MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B |
185 MT9M111_CTXT_CTRL_READ_MODE_B | MT9M111_CTXT_CTRL_VBLANK_SEL_B |
186 MT9M111_CTXT_CTRL_HBLANK_SEL_B,
187};
188
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300189/* MT9M111 has only one fixed colorspace per pixelcode */
190struct mt9m111_datafmt {
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300191 u32 code;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300192 enum v4l2_colorspace colorspace;
193};
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300194
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300195static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
Robert Jarzmik1a412fa2016-09-06 06:04:12 -0300196 {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB},
197 {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_SRGB},
198 {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB},
199 {MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_SRGB},
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300200 {MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
201 {MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
202 {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
203 {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
204 {MEDIA_BUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
205 {MEDIA_BUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
206 {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
207 {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300208};
209
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300210struct mt9m111 {
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300211 struct v4l2_subdev subdev;
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300212 struct v4l2_ctrl_handler hdl;
213 struct v4l2_ctrl *gain;
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300214 struct mt9m111_context *ctx;
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300215 struct v4l2_rect rect; /* cropping rectangle */
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -0300216 struct v4l2_clk *clk;
Ricardo Ribaldaf90580c2013-11-26 05:31:42 -0300217 unsigned int width; /* output */
218 unsigned int height; /* sizes */
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300219 struct mutex power_lock; /* lock to protect power_count */
220 int power_count;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300221 const struct mt9m111_datafmt *fmt;
Michael Grzeschik096b7032011-07-19 09:26:35 -0300222 int lastpage; /* PageMap cache value */
Akinobu Mita90411ce2018-01-03 13:22:45 -0500223#ifdef CONFIG_MEDIA_CONTROLLER
224 struct media_pad pad;
225#endif
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300226};
227
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300228/* Find a data format by a pixel code */
229static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111,
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300230 u32 code)
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300231{
232 int i;
233 for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++)
234 if (mt9m111_colour_fmts[i].code == code)
235 return mt9m111_colour_fmts + i;
236
237 return mt9m111->fmt;
238}
239
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300240static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
241{
242 return container_of(i2c_get_clientdata(client), struct mt9m111, subdev);
243}
244
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300245static int reg_page_map_set(struct i2c_client *client, const u16 reg)
246{
247 int ret;
248 u16 page;
Michael Grzeschik096b7032011-07-19 09:26:35 -0300249 struct mt9m111 *mt9m111 = to_mt9m111(client);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300250
251 page = (reg >> 8);
Michael Grzeschik096b7032011-07-19 09:26:35 -0300252 if (page == mt9m111->lastpage)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300253 return 0;
254 if (page > 2)
255 return -EINVAL;
256
Jonathan Cameron3f877042011-10-21 09:30:25 -0300257 ret = i2c_smbus_write_word_swapped(client, MT9M111_PAGE_MAP, page);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300258 if (!ret)
Michael Grzeschik096b7032011-07-19 09:26:35 -0300259 mt9m111->lastpage = page;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300260 return ret;
261}
262
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300263static int mt9m111_reg_read(struct i2c_client *client, const u16 reg)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300264{
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300265 int ret;
266
267 ret = reg_page_map_set(client, reg);
268 if (!ret)
Jonathan Cameron3f877042011-10-21 09:30:25 -0300269 ret = i2c_smbus_read_word_swapped(client, reg & 0xff);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300270
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300271 dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300272 return ret;
273}
274
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300275static int mt9m111_reg_write(struct i2c_client *client, const u16 reg,
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300276 const u16 data)
277{
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300278 int ret;
279
280 ret = reg_page_map_set(client, reg);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300281 if (!ret)
Jonathan Cameron3f877042011-10-21 09:30:25 -0300282 ret = i2c_smbus_write_word_swapped(client, reg & 0xff, data);
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300283 dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300284 return ret;
285}
286
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300287static int mt9m111_reg_set(struct i2c_client *client, const u16 reg,
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300288 const u16 data)
289{
290 int ret;
291
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300292 ret = mt9m111_reg_read(client, reg);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300293 if (ret >= 0)
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300294 ret = mt9m111_reg_write(client, reg, ret | data);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300295 return ret;
296}
297
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300298static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300299 const u16 data)
300{
301 int ret;
302
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300303 ret = mt9m111_reg_read(client, reg);
Michael Grzeschik9c56cbf2011-07-12 12:39:03 -0300304 if (ret >= 0)
305 ret = mt9m111_reg_write(client, reg, ret & ~data);
306 return ret;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300307}
308
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300309static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
310 const u16 data, const u16 mask)
311{
312 int ret;
313
314 ret = mt9m111_reg_read(client, reg);
315 if (ret >= 0)
316 ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
317 return ret;
318}
319
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300320static int mt9m111_set_context(struct mt9m111 *mt9m111,
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300321 struct mt9m111_context *ctx)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300322{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300323 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300324 return reg_write(CONTEXT_CONTROL, ctx->control);
325}
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300326
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300327static int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111,
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300328 struct mt9m111_context *ctx, struct v4l2_rect *rect,
329 unsigned int width, unsigned int height)
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300330{
331 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300332 int ret = mt9m111_reg_write(client, ctx->reducer_xzoom, rect->width);
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300333 if (!ret)
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300334 ret = mt9m111_reg_write(client, ctx->reducer_yzoom, rect->height);
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300335 if (!ret)
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300336 ret = mt9m111_reg_write(client, ctx->reducer_xsize, width);
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300337 if (!ret)
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300338 ret = mt9m111_reg_write(client, ctx->reducer_ysize, height);
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300339 return ret;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300340}
341
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300342static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect,
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300343 int width, int height, u32 code)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300344{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300345 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300346 int ret;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300347
Guennadi Liakhovetski09e231b2009-03-13 06:08:20 -0300348 ret = reg_write(COLUMN_START, rect->left);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300349 if (!ret)
Guennadi Liakhovetski09e231b2009-03-13 06:08:20 -0300350 ret = reg_write(ROW_START, rect->top);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300351
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300352 if (!ret)
353 ret = reg_write(WINDOW_WIDTH, rect->width);
354 if (!ret)
355 ret = reg_write(WINDOW_HEIGHT, rect->height);
356
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300357 if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) {
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300358 /* IFP in use, down-scaling possible */
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300359 if (!ret)
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300360 ret = mt9m111_setup_rect_ctx(mt9m111, &context_b,
361 rect, width, height);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300362 if (!ret)
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300363 ret = mt9m111_setup_rect_ctx(mt9m111, &context_a,
364 rect, width, height);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300365 }
366
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300367 dev_dbg(&client->dev, "%s(%x): %ux%u@%u:%u -> %ux%u = %d\n",
368 __func__, code, rect->width, rect->height, rect->left, rect->top,
369 width, height, ret);
370
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300371 return ret;
372}
373
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300374static int mt9m111_enable(struct mt9m111 *mt9m111)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300375{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300376 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Guennadi Liakhovetskia650bf12011-11-03 08:19:18 -0300377 return reg_write(RESET, MT9M111_RESET_CHIP_ENABLE);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300378}
379
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300380static int mt9m111_reset(struct mt9m111 *mt9m111)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300381{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300382 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300383 int ret;
384
385 ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300386 if (!ret)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300387 ret = reg_set(RESET, MT9M111_RESET_RESET_SOC);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300388 if (!ret)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300389 ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
390 | MT9M111_RESET_RESET_SOC);
Antonio Ospiteafb13682009-02-23 12:13:24 -0300391
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300392 return ret;
393}
394
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200395static int mt9m111_set_selection(struct v4l2_subdev *sd,
396 struct v4l2_subdev_pad_config *cfg,
397 struct v4l2_subdev_selection *sel)
Guennadi Liakhovetski09e231b2009-03-13 06:08:20 -0300398{
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200399 struct i2c_client *client = v4l2_get_subdevdata(sd);
400 struct mt9m111 *mt9m111 = to_mt9m111(client);
401 struct v4l2_rect rect = sel->r;
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300402 int width, height;
Robert Jarzmik5d7cc012016-09-06 06:04:11 -0300403 int ret, align = 0;
Guennadi Liakhovetski09e231b2009-03-13 06:08:20 -0300404
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200405 if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
406 sel->target != V4L2_SEL_TGT_CROP)
Michael Grzeschik6b6d33c2010-08-03 07:57:44 -0300407 return -EINVAL;
408
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300409 if (mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
410 mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) {
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300411 /* Bayer format - even size lengths */
Robert Jarzmik5d7cc012016-09-06 06:04:11 -0300412 align = 1;
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300413 /* Let the user play with the starting pixel */
414 }
415
416 /* FIXME: the datasheet doesn't specify minimum sizes */
Robert Jarzmik5d7cc012016-09-06 06:04:11 -0300417 v4l_bound_align_image(&rect.width, 2, MT9M111_MAX_WIDTH, align,
418 &rect.height, 2, MT9M111_MAX_HEIGHT, align, 0);
419 rect.left = clamp(rect.left, MT9M111_MIN_DARK_COLS,
420 MT9M111_MIN_DARK_COLS + MT9M111_MAX_WIDTH -
421 (__s32)rect.width);
422 rect.top = clamp(rect.top, MT9M111_MIN_DARK_ROWS,
423 MT9M111_MIN_DARK_ROWS + MT9M111_MAX_HEIGHT -
424 (__s32)rect.height);
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300425
426 width = min(mt9m111->width, rect.width);
427 height = min(mt9m111->height, rect.height);
428
429 ret = mt9m111_setup_geometry(mt9m111, &rect, width, height, mt9m111->fmt->code);
430 if (!ret) {
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300431 mt9m111->rect = rect;
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300432 mt9m111->width = width;
433 mt9m111->height = height;
434 }
435
Guennadi Liakhovetski09e231b2009-03-13 06:08:20 -0300436 return ret;
437}
438
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200439static int mt9m111_get_selection(struct v4l2_subdev *sd,
440 struct v4l2_subdev_pad_config *cfg,
441 struct v4l2_subdev_selection *sel)
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300442{
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200443 struct i2c_client *client = v4l2_get_subdevdata(sd);
444 struct mt9m111 *mt9m111 = to_mt9m111(client);
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300445
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200446 if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
Michael Grzeschik6b6d33c2010-08-03 07:57:44 -0300447 return -EINVAL;
448
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200449 switch (sel->target) {
450 case V4L2_SEL_TGT_CROP_BOUNDS:
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200451 sel->r.left = MT9M111_MIN_DARK_COLS;
452 sel->r.top = MT9M111_MIN_DARK_ROWS;
453 sel->r.width = MT9M111_MAX_WIDTH;
454 sel->r.height = MT9M111_MAX_HEIGHT;
455 return 0;
456 case V4L2_SEL_TGT_CROP:
457 sel->r = mt9m111->rect;
458 return 0;
459 default:
460 return -EINVAL;
461 }
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300462}
463
Hans Verkuilda298c62015-04-09 04:02:34 -0300464static int mt9m111_get_fmt(struct v4l2_subdev *sd,
465 struct v4l2_subdev_pad_config *cfg,
466 struct v4l2_subdev_format *format)
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300467{
Hans Verkuilda298c62015-04-09 04:02:34 -0300468 struct v4l2_mbus_framefmt *mf = &format->format;
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300469 struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300470
Hans Verkuilda298c62015-04-09 04:02:34 -0300471 if (format->pad)
472 return -EINVAL;
473
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300474 mf->width = mt9m111->width;
475 mf->height = mt9m111->height;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300476 mf->code = mt9m111->fmt->code;
Michael Grzeschik01f5a392010-08-03 07:57:45 -0300477 mf->colorspace = mt9m111->fmt->colorspace;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300478 mf->field = V4L2_FIELD_NONE;
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300479
480 return 0;
481}
482
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300483static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300484 u32 code)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300485{
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300486 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
487 u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
488 MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
489 MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
490 MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
491 MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
492 MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300493 int ret;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300494
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300495 switch (code) {
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300496 case MEDIA_BUS_FMT_SBGGR8_1X8:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300497 data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
498 MT9M111_OUTFMT_RGB;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300499 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300500 case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300501 data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300502 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300503 case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300504 data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
505 MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
506 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300507 case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300508 data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300509 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300510 case MEDIA_BUS_FMT_RGB565_2X8_LE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300511 data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
512 MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
513 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300514 case MEDIA_BUS_FMT_RGB565_2X8_BE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300515 data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
516 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300517 case MEDIA_BUS_FMT_BGR565_2X8_BE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300518 data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
519 MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
520 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300521 case MEDIA_BUS_FMT_BGR565_2X8_LE:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300522 data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
523 MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
524 MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300525 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300526 case MEDIA_BUS_FMT_UYVY8_2X8:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300527 data_outfmt2 = 0;
Robert Jarzmik88f4b892008-12-17 14:05:31 -0300528 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300529 case MEDIA_BUS_FMT_VYUY8_2X8:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300530 data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
Robert Jarzmik88f4b892008-12-17 14:05:31 -0300531 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300532 case MEDIA_BUS_FMT_YUYV8_2X8:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300533 data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
Robert Jarzmik88f4b892008-12-17 14:05:31 -0300534 break;
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300535 case MEDIA_BUS_FMT_YVYU8_2X8:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300536 data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
537 MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300538 break;
539 default:
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300540 dev_err(&client->dev, "Pixel format not handled: %x\n", code);
541 return -EINVAL;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300542 }
543
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300544 ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2,
545 data_outfmt2, mask_outfmt2);
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300546 if (!ret)
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300547 ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2,
548 data_outfmt2, mask_outfmt2);
Michael Grzeschik7c58e7d2011-07-12 12:39:05 -0300549
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300550 return ret;
551}
552
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300553static int mt9m111_set_fmt(struct v4l2_subdev *sd,
554 struct v4l2_subdev_pad_config *cfg,
555 struct v4l2_subdev_format *format)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300556{
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300557 struct v4l2_mbus_framefmt *mf = &format->format;
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300558 struct i2c_client *client = v4l2_get_subdevdata(sd);
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300559 struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300560 const struct mt9m111_datafmt *fmt;
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300561 struct v4l2_rect *rect = &mt9m111->rect;
562 bool bayer;
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300563 int ret;
564
565 if (format->pad)
566 return -EINVAL;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300567
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300568 fmt = mt9m111_find_datafmt(mt9m111, mf->code);
569
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300570 bayer = fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
571 fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE;
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300572
573 /*
574 * With Bayer format enforce even side lengths, but let the user play
575 * with the starting pixel
576 */
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300577 if (bayer) {
578 rect->width = ALIGN(rect->width, 2);
579 rect->height = ALIGN(rect->height, 2);
580 }
Guennadi Liakhovetski64f59052008-12-18 11:51:55 -0300581
Boris BREZILLONf5fe58f2014-11-10 14:28:29 -0300582 if (fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) {
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300583 /* IFP bypass mode, no scaling */
584 mf->width = rect->width;
585 mf->height = rect->height;
586 } else {
587 /* No upscaling */
588 if (mf->width > rect->width)
589 mf->width = rect->width;
590 if (mf->height > rect->height)
591 mf->height = rect->height;
592 }
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -0300593
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300594 dev_dbg(&client->dev, "%s(): %ux%u, code=%x\n", __func__,
595 mf->width, mf->height, fmt->code);
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300596
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300597 mf->code = fmt->code;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300598 mf->colorspace = fmt->colorspace;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300599
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300600 if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
601 cfg->try_fmt = *mf;
602 return 0;
603 }
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300604
605 ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code);
606 if (!ret)
607 ret = mt9m111_set_pixfmt(mt9m111, mf->code);
608 if (!ret) {
609 mt9m111->width = mf->width;
610 mt9m111->height = mf->height;
611 mt9m111->fmt = fmt;
612 }
613
614 return ret;
615}
616
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300617#ifdef CONFIG_VIDEO_ADV_DEBUG
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300618static int mt9m111_g_register(struct v4l2_subdev *sd,
619 struct v4l2_dbg_register *reg)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300620{
Laurent Pinchartc4ce6d12010-07-30 17:24:54 -0300621 struct i2c_client *client = v4l2_get_subdevdata(sd);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300622 int val;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300623
Hans Verkuil6be89da2013-05-29 06:59:50 -0300624 if (reg->reg > 0x2ff)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300625 return -EINVAL;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300626
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300627 val = mt9m111_reg_read(client, reg->reg);
Hans Verkuilaecde8b52008-12-30 07:14:19 -0300628 reg->size = 2;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300629 reg->val = (u64)val;
630
631 if (reg->val > 0xffff)
632 return -EIO;
633
634 return 0;
635}
636
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300637static int mt9m111_s_register(struct v4l2_subdev *sd,
Hans Verkuil977ba3b12013-03-24 08:28:46 -0300638 const struct v4l2_dbg_register *reg)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300639{
Laurent Pinchartc4ce6d12010-07-30 17:24:54 -0300640 struct i2c_client *client = v4l2_get_subdevdata(sd);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300641
Hans Verkuil6be89da2013-05-29 06:59:50 -0300642 if (reg->reg > 0x2ff)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300643 return -EINVAL;
644
Guennadi Liakhovetski9538e1c2009-04-24 12:57:01 -0300645 if (mt9m111_reg_write(client, reg->reg, reg->val) < 0)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300646 return -EIO;
647
648 return 0;
649}
650#endif
651
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300652static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300653{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300654 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300655 int ret;
656
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300657 if (flip)
658 ret = mt9m111_reg_set(client, mt9m111->ctx->read_mode, mask);
659 else
660 ret = mt9m111_reg_clear(client, mt9m111->ctx->read_mode, mask);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300661
662 return ret;
663}
664
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300665static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300666{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300667 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
roel kluin0f28b792008-12-17 14:01:07 -0300668 int data;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300669
670 data = reg_read(GLOBAL_GAIN);
671 if (data >= 0)
roel kluin0f28b792008-12-17 14:01:07 -0300672 return (data & 0x2f) * (1 << ((data >> 10) & 1)) *
673 (1 << ((data >> 9) & 1));
674 return data;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300675}
roel kluin0f28b792008-12-17 14:01:07 -0300676
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300677static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300678{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300679 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300680 u16 val;
681
682 if (gain > 63 * 2 * 2)
683 return -EINVAL;
684
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300685 if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
686 val = (1 << 10) | (1 << 9) | (gain / 4);
687 else if ((gain >= 64) && (gain < 64 * 2))
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300688 val = (1 << 9) | (gain / 2);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300689 else
690 val = gain;
691
692 return reg_write(GLOBAL_GAIN, val);
693}
694
Benoît Thébaudeaucbaa5c52013-02-26 15:32:49 -0300695static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int val)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300696{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300697 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300698
Benoît Thébaudeaucbaa5c52013-02-26 15:32:49 -0300699 if (val == V4L2_EXPOSURE_AUTO)
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300700 return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
701 return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300702}
Robert Jarzmik39bf3722008-12-18 11:29:05 -0300703
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300704static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
Robert Jarzmik39bf3722008-12-18 11:29:05 -0300705{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300706 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Robert Jarzmik39bf3722008-12-18 11:29:05 -0300707
708 if (on)
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300709 return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
710 return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
Robert Jarzmik39bf3722008-12-18 11:29:05 -0300711}
712
Akinobu Mita74e08732018-01-03 13:22:47 -0500713static const char * const mt9m111_test_pattern_menu[] = {
714 "Disabled",
715 "Vertical monochrome gradient",
716 "Flat color type 1",
717 "Flat color type 2",
718 "Flat color type 3",
719 "Flat color type 4",
720 "Flat color type 5",
721 "Color bar",
722};
723
724static int mt9m111_set_test_pattern(struct mt9m111 *mt9m111, int val)
725{
726 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
727
728 return mt9m111_reg_mask(client, MT9M111_TPG_CTRL, val,
729 MT9M111_TPG_SEL_MASK);
730}
731
Akinobu Mitadde64f72018-11-12 11:00:49 -0500732static int mt9m111_set_colorfx(struct mt9m111 *mt9m111, int val)
733{
734 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
735 static const struct v4l2_control colorfx[] = {
736 { V4L2_COLORFX_NONE, 0 },
737 { V4L2_COLORFX_BW, 1 },
738 { V4L2_COLORFX_SEPIA, 2 },
739 { V4L2_COLORFX_NEGATIVE, 3 },
740 { V4L2_COLORFX_SOLARIZATION, 4 },
741 };
742 int i;
743
744 for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
745 if (colorfx[i].id == val) {
746 return mt9m111_reg_mask(client, MT9M111_EFFECTS_MODE,
747 colorfx[i].value,
748 MT9M111_EFFECTS_MODE_MASK);
749 }
750 }
751
752 return -EINVAL;
753}
754
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300755static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300756{
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300757 struct mt9m111 *mt9m111 = container_of(ctrl->handler,
758 struct mt9m111, hdl);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300759
760 switch (ctrl->id) {
761 case V4L2_CID_VFLIP:
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300762 return mt9m111_set_flip(mt9m111, ctrl->val,
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300763 MT9M111_RMB_MIRROR_ROWS);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300764 case V4L2_CID_HFLIP:
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300765 return mt9m111_set_flip(mt9m111, ctrl->val,
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300766 MT9M111_RMB_MIRROR_COLS);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300767 case V4L2_CID_GAIN:
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300768 return mt9m111_set_global_gain(mt9m111, ctrl->val);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300769 case V4L2_CID_EXPOSURE_AUTO:
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300770 return mt9m111_set_autoexposure(mt9m111, ctrl->val);
Robert Jarzmik39bf3722008-12-18 11:29:05 -0300771 case V4L2_CID_AUTO_WHITE_BALANCE:
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300772 return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
Akinobu Mita74e08732018-01-03 13:22:47 -0500773 case V4L2_CID_TEST_PATTERN:
774 return mt9m111_set_test_pattern(mt9m111, ctrl->val);
Akinobu Mitadde64f72018-11-12 11:00:49 -0500775 case V4L2_CID_COLORFX:
776 return mt9m111_set_colorfx(mt9m111, ctrl->val);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300777 }
778
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300779 return -EINVAL;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300780}
781
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300782static int mt9m111_suspend(struct mt9m111 *mt9m111)
Guennadi Liakhovetski96c75392009-08-25 11:53:23 -0300783{
Guennadi Liakhovetskia650bf12011-11-03 08:19:18 -0300784 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
785 int ret;
786
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300787 v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
Guennadi Liakhovetski96c75392009-08-25 11:53:23 -0300788
Guennadi Liakhovetskia650bf12011-11-03 08:19:18 -0300789 ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
790 if (!ret)
791 ret = reg_set(RESET, MT9M111_RESET_RESET_SOC |
792 MT9M111_RESET_OUTPUT_DISABLE |
793 MT9M111_RESET_ANALOG_STANDBY);
794 if (!ret)
795 ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE);
796
797 return ret;
Guennadi Liakhovetski96c75392009-08-25 11:53:23 -0300798}
799
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300800static void mt9m111_restore_state(struct mt9m111 *mt9m111)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300801{
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300802 mt9m111_set_context(mt9m111, mt9m111->ctx);
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300803 mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
Guennadi Liakhovetskida673e62011-11-04 15:13:18 -0300804 mt9m111_setup_geometry(mt9m111, &mt9m111->rect,
805 mt9m111->width, mt9m111->height, mt9m111->fmt->code);
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300806 v4l2_ctrl_handler_setup(&mt9m111->hdl);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300807}
808
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300809static int mt9m111_resume(struct mt9m111 *mt9m111)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300810{
Guennadi Liakhovetskia650bf12011-11-03 08:19:18 -0300811 int ret = mt9m111_enable(mt9m111);
812 if (!ret)
813 ret = mt9m111_reset(mt9m111);
814 if (!ret)
815 mt9m111_restore_state(mt9m111);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300816
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300817 return ret;
818}
819
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300820static int mt9m111_init(struct mt9m111 *mt9m111)
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300821{
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300822 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300823 int ret;
824
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300825 ret = mt9m111_enable(mt9m111);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300826 if (!ret)
Guennadi Liakhovetski2768cbb2011-06-07 06:47:30 -0300827 ret = mt9m111_reset(mt9m111);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300828 if (!ret)
Guennadi Liakhovetski47921932011-11-03 08:14:56 -0300829 ret = mt9m111_set_context(mt9m111, mt9m111->ctx);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300830 if (ret)
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -0300831 dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
Guennadi Liakhovetski506c6292008-08-14 12:03:49 -0300832 return ret;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300833}
834
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300835static int mt9m111_power_on(struct mt9m111 *mt9m111)
836{
837 struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300838 int ret;
839
Robert Jarzmik5d7cc012016-09-06 06:04:11 -0300840 ret = v4l2_clk_enable(mt9m111->clk);
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300841 if (ret < 0)
842 return ret;
843
844 ret = mt9m111_resume(mt9m111);
845 if (ret < 0) {
846 dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret);
Robert Jarzmik5d7cc012016-09-06 06:04:11 -0300847 v4l2_clk_disable(mt9m111->clk);
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300848 }
849
850 return ret;
851}
852
853static void mt9m111_power_off(struct mt9m111 *mt9m111)
854{
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300855 mt9m111_suspend(mt9m111);
Robert Jarzmik5d7cc012016-09-06 06:04:11 -0300856 v4l2_clk_disable(mt9m111->clk);
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300857}
858
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300859static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
860{
861 struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300862 int ret = 0;
863
864 mutex_lock(&mt9m111->power_lock);
865
866 /*
867 * If the power count is modified from 0 to != 0 or from != 0 to 0,
868 * update the power state.
869 */
870 if (mt9m111->power_count == !on) {
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300871 if (on)
872 ret = mt9m111_power_on(mt9m111);
873 else
874 mt9m111_power_off(mt9m111);
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300875 }
876
Laurent Pinchart4ec10ba2012-07-20 10:19:50 -0300877 if (!ret) {
878 /* Update the power count. */
879 mt9m111->power_count += on ? 1 : -1;
880 WARN_ON(mt9m111->power_count < 0);
881 }
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300882
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300883 mutex_unlock(&mt9m111->power_lock);
884 return ret;
885}
886
Hans Verkuilaf8425c2011-09-07 06:56:57 -0300887static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
888 .s_ctrl = mt9m111_s_ctrl,
889};
890
Julia Lawall39229622017-08-08 06:58:30 -0400891static const struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
Guennadi Liakhovetski14c5ea92011-06-05 08:27:39 -0300892 .s_power = mt9m111_s_power,
Akinobu Mita329d9e352018-11-12 11:00:48 -0500893 .log_status = v4l2_ctrl_subdev_log_status,
894 .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
895 .unsubscribe_event = v4l2_event_subdev_unsubscribe,
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300896#ifdef CONFIG_VIDEO_ADV_DEBUG
897 .g_register = mt9m111_g_register,
898 .s_register = mt9m111_s_register,
899#endif
900};
901
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300902static int mt9m111_enum_mbus_code(struct v4l2_subdev *sd,
903 struct v4l2_subdev_pad_config *cfg,
904 struct v4l2_subdev_mbus_code_enum *code)
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300905{
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300906 if (code->pad || code->index >= ARRAY_SIZE(mt9m111_colour_fmts))
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300907 return -EINVAL;
908
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300909 code->code = mt9m111_colour_fmts[code->index].code;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -0300910 return 0;
911}
912
Guennadi Liakhovetski0c0b4462011-07-26 11:48:29 -0300913static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
914 struct v4l2_mbus_config *cfg)
915{
Guennadi Liakhovetski0c0b4462011-07-26 11:48:29 -0300916 cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
917 V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
918 V4L2_MBUS_DATA_ACTIVE_HIGH;
919 cfg->type = V4L2_MBUS_PARALLEL;
Guennadi Liakhovetski0c0b4462011-07-26 11:48:29 -0300920
921 return 0;
922}
923
Julia Lawall39229622017-08-08 06:58:30 -0400924static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
Guennadi Liakhovetski0c0b4462011-07-26 11:48:29 -0300925 .g_mbus_config = mt9m111_g_mbus_config,
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300926};
927
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300928static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = {
929 .enum_mbus_code = mt9m111_enum_mbus_code,
Hans Verkuil10d5509c2015-12-14 08:25:32 -0200930 .get_selection = mt9m111_get_selection,
931 .set_selection = mt9m111_set_selection,
Hans Verkuilda298c62015-04-09 04:02:34 -0300932 .get_fmt = mt9m111_get_fmt,
Hans Verkuil717fd5b2015-04-09 06:24:36 -0300933 .set_fmt = mt9m111_set_fmt,
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300934};
935
Julia Lawall39229622017-08-08 06:58:30 -0400936static const struct v4l2_subdev_ops mt9m111_subdev_ops = {
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300937 .core = &mt9m111_subdev_core_ops,
938 .video = &mt9m111_subdev_video_ops,
Hans Verkuilebcff5f2015-04-09 04:01:33 -0300939 .pad = &mt9m111_subdev_pad_ops,
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -0300940};
941
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300942/*
943 * Interface active, can use i2c. If it fails, it can indeed mean, that
944 * this wasn't our capture interface, so, we wait for the right one
945 */
946static int mt9m111_video_probe(struct i2c_client *client)
947{
948 struct mt9m111 *mt9m111 = to_mt9m111(client);
949 s32 data;
950 int ret;
951
952 ret = mt9m111_s_power(&mt9m111->subdev, 1);
953 if (ret < 0)
954 return ret;
955
956 data = reg_read(CHIP_VERSION);
957
958 switch (data) {
959 case 0x143a: /* MT9M111 or MT9M131 */
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300960 dev_info(&client->dev,
961 "Detected a MT9M111/MT9M131 chip ID %x\n", data);
962 break;
963 case 0x148c: /* MT9M112 */
Laurent Pinchart4bbc6d52012-07-18 10:54:04 -0300964 dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
965 break;
966 default:
967 dev_err(&client->dev,
968 "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
969 data);
970 ret = -ENODEV;
971 goto done;
972 }
973
974 ret = mt9m111_init(mt9m111);
975 if (ret)
976 goto done;
977
978 ret = v4l2_ctrl_handler_setup(&mt9m111->hdl);
979
980done:
981 mt9m111_s_power(&mt9m111->subdev, 0);
982 return ret;
983}
984
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300985static int mt9m111_probe(struct i2c_client *client,
986 const struct i2c_device_id *did)
987{
988 struct mt9m111 *mt9m111;
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300989 struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300990 int ret;
991
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300992 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
993 dev_warn(&adapter->dev,
994 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
995 return -EIO;
996 }
997
Guennadi Liakhovetski70e176a2012-12-21 10:28:43 -0300998 mt9m111 = devm_kzalloc(&client->dev, sizeof(struct mt9m111), GFP_KERNEL);
Robert Jarzmik77110ab2008-08-14 12:02:51 -0300999 if (!mt9m111)
1000 return -ENOMEM;
1001
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001002 mt9m111->clk = v4l2_clk_get(&client->dev, "mclk");
1003 if (IS_ERR(mt9m111->clk))
Fabio Estevambddb4b52017-08-27 13:30:36 -03001004 return PTR_ERR(mt9m111->clk);
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001005
Guennadi Liakhovetski4a1313c2013-03-12 08:40:37 -03001006 /* Default HIGHPOWER context */
1007 mt9m111->ctx = &context_b;
1008
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -03001009 v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
Akinobu Mita329d9e352018-11-12 11:00:48 -05001010 mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
1011 V4L2_SUBDEV_FL_HAS_EVENTS;
Akinobu Mita5ed8c222018-01-03 13:22:44 -05001012
Akinobu Mitadde64f72018-11-12 11:00:49 -05001013 v4l2_ctrl_handler_init(&mt9m111->hdl, 7);
Hans Verkuilaf8425c2011-09-07 06:56:57 -03001014 v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
1015 V4L2_CID_VFLIP, 0, 1, 1, 0);
1016 v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
1017 V4L2_CID_HFLIP, 0, 1, 1, 0);
1018 v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
1019 V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
1020 mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
1021 V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
1022 v4l2_ctrl_new_std_menu(&mt9m111->hdl,
1023 &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
1024 V4L2_EXPOSURE_AUTO);
Akinobu Mita74e08732018-01-03 13:22:47 -05001025 v4l2_ctrl_new_std_menu_items(&mt9m111->hdl,
1026 &mt9m111_ctrl_ops, V4L2_CID_TEST_PATTERN,
1027 ARRAY_SIZE(mt9m111_test_pattern_menu) - 1, 0, 0,
1028 mt9m111_test_pattern_menu);
Akinobu Mitadde64f72018-11-12 11:00:49 -05001029 v4l2_ctrl_new_std_menu(&mt9m111->hdl, &mt9m111_ctrl_ops,
1030 V4L2_CID_COLORFX, V4L2_COLORFX_SOLARIZATION,
1031 ~(BIT(V4L2_COLORFX_NONE) |
1032 BIT(V4L2_COLORFX_BW) |
1033 BIT(V4L2_COLORFX_SEPIA) |
1034 BIT(V4L2_COLORFX_NEGATIVE) |
1035 BIT(V4L2_COLORFX_SOLARIZATION)),
1036 V4L2_COLORFX_NONE);
Hans Verkuilaf8425c2011-09-07 06:56:57 -03001037 mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001038 if (mt9m111->hdl.error) {
1039 ret = mt9m111->hdl.error;
1040 goto out_clkput;
1041 }
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001042
Akinobu Mita90411ce2018-01-03 13:22:45 -05001043#ifdef CONFIG_MEDIA_CONTROLLER
1044 mt9m111->pad.flags = MEDIA_PAD_FL_SOURCE;
1045 mt9m111->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1046 ret = media_entity_pads_init(&mt9m111->subdev.entity, 1, &mt9m111->pad);
1047 if (ret < 0)
1048 goto out_hdlfree;
1049#endif
1050
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001051 /* Second stage probe - when a capture adapter is there */
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -03001052 mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
1053 mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
1054 mt9m111->rect.width = MT9M111_MAX_WIDTH;
1055 mt9m111->rect.height = MT9M111_MAX_HEIGHT;
Guennadi Liakhovetski760697b2009-12-11 11:46:49 -03001056 mt9m111->fmt = &mt9m111_colour_fmts[0];
Guennadi Liakhovetski14178aa2011-09-21 15:16:30 -03001057 mt9m111->lastpage = -1;
Guennadi Liakhovetski6b806e32011-11-03 08:12:00 -03001058 mutex_init(&mt9m111->power_lock);
Guennadi Liakhovetski6a6c8782009-08-25 11:50:46 -03001059
Guennadi Liakhovetski14178aa2011-09-21 15:16:30 -03001060 ret = mt9m111_video_probe(client);
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001061 if (ret < 0)
Akinobu Mita90411ce2018-01-03 13:22:45 -05001062 goto out_entityclean;
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001063
1064 mt9m111->subdev.dev = &client->dev;
1065 ret = v4l2_async_register_subdev(&mt9m111->subdev);
1066 if (ret < 0)
Akinobu Mita90411ce2018-01-03 13:22:45 -05001067 goto out_entityclean;
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001068
1069 return 0;
1070
Akinobu Mita90411ce2018-01-03 13:22:45 -05001071out_entityclean:
1072#ifdef CONFIG_MEDIA_CONTROLLER
1073 media_entity_cleanup(&mt9m111->subdev.entity);
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001074out_hdlfree:
Akinobu Mita90411ce2018-01-03 13:22:45 -05001075#endif
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001076 v4l2_ctrl_handler_free(&mt9m111->hdl);
1077out_clkput:
1078 v4l2_clk_put(mt9m111->clk);
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001079
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001080 return ret;
1081}
1082
1083static int mt9m111_remove(struct i2c_client *client)
1084{
Guennadi Liakhovetski979ea1d2009-08-25 11:43:33 -03001085 struct mt9m111 *mt9m111 = to_mt9m111(client);
Guennadi Liakhovetski40e2e092009-08-25 11:28:22 -03001086
Guennadi Liakhovetskief6672e2013-07-30 04:35:18 -03001087 v4l2_async_unregister_subdev(&mt9m111->subdev);
Akinobu Mita90411ce2018-01-03 13:22:45 -05001088 media_entity_cleanup(&mt9m111->subdev.entity);
Guennadi Liakhovetski9aea4702012-12-21 13:01:55 -03001089 v4l2_clk_put(mt9m111->clk);
Hans Verkuilaf8425c2011-09-07 06:56:57 -03001090 v4l2_ctrl_handler_free(&mt9m111->hdl);
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001091
1092 return 0;
1093}
Robert Jarzmikc5f176d2014-06-21 19:19:54 -03001094static const struct of_device_id mt9m111_of_match[] = {
1095 { .compatible = "micron,mt9m111", },
1096 {},
1097};
1098MODULE_DEVICE_TABLE(of, mt9m111_of_match);
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001099
1100static const struct i2c_device_id mt9m111_id[] = {
1101 { "mt9m111", 0 },
1102 { }
1103};
1104MODULE_DEVICE_TABLE(i2c, mt9m111_id);
1105
1106static struct i2c_driver mt9m111_i2c_driver = {
1107 .driver = {
1108 .name = "mt9m111",
Robert Jarzmikc5f176d2014-06-21 19:19:54 -03001109 .of_match_table = of_match_ptr(mt9m111_of_match),
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001110 },
1111 .probe = mt9m111_probe,
1112 .remove = mt9m111_remove,
1113 .id_table = mt9m111_id,
1114};
1115
Axel Linc6e8d862012-02-12 06:56:32 -03001116module_i2c_driver(mt9m111_i2c_driver);
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001117
Philipp Wiesnerc8cf0782010-08-03 07:57:39 -03001118MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
Robert Jarzmik77110ab2008-08-14 12:02:51 -03001119MODULE_AUTHOR("Robert Jarzmik");
1120MODULE_LICENSE("GPL");