1f5fbb83fSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
2ad85094bSMauro Carvalho Chehab /*
3ad85094bSMauro Carvalho Chehab  * Support for GalaxyCore GC0310 VGA camera sensor.
4ad85094bSMauro Carvalho Chehab  *
5ad85094bSMauro Carvalho Chehab  * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
6ad85094bSMauro Carvalho Chehab  *
7ad85094bSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
8ad85094bSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License version
9ad85094bSMauro Carvalho Chehab  * 2 as published by the Free Software Foundation.
10ad85094bSMauro Carvalho Chehab  *
11ad85094bSMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
12ad85094bSMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13ad85094bSMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14ad85094bSMauro Carvalho Chehab  * GNU General Public License for more details.
15ad85094bSMauro Carvalho Chehab  *
16ad85094bSMauro Carvalho Chehab  */
17ad85094bSMauro Carvalho Chehab 
18ad85094bSMauro Carvalho Chehab #include <linux/module.h>
19ad85094bSMauro Carvalho Chehab #include <linux/types.h>
20ad85094bSMauro Carvalho Chehab #include <linux/kernel.h>
21ad85094bSMauro Carvalho Chehab #include <linux/mm.h>
22ad85094bSMauro Carvalho Chehab #include <linux/string.h>
23ad85094bSMauro Carvalho Chehab #include <linux/errno.h>
24ad85094bSMauro Carvalho Chehab #include <linux/init.h>
25ad85094bSMauro Carvalho Chehab #include <linux/kmod.h>
26ad85094bSMauro Carvalho Chehab #include <linux/device.h>
27ad85094bSMauro Carvalho Chehab #include <linux/delay.h>
28ad85094bSMauro Carvalho Chehab #include <linux/slab.h>
29ad85094bSMauro Carvalho Chehab #include <linux/i2c.h>
30ad85094bSMauro Carvalho Chehab #include <linux/moduleparam.h>
31ad85094bSMauro Carvalho Chehab #include <media/v4l2-device.h>
32ad85094bSMauro Carvalho Chehab #include <linux/io.h>
33ad85094bSMauro Carvalho Chehab #include "../include/linux/atomisp_gmin_platform.h"
34ad85094bSMauro Carvalho Chehab 
35ad85094bSMauro Carvalho Chehab #include "gc0310.h"
36ad85094bSMauro Carvalho Chehab 
37ad85094bSMauro Carvalho Chehab /*
38ad85094bSMauro Carvalho Chehab  * gc0310_write_reg_array - Initializes a list of GC0310 registers
39ad85094bSMauro Carvalho Chehab  * @client: i2c driver client structure
40ad85094bSMauro Carvalho Chehab  * @reglist: list of registers to be written
41bfe06aeeSHans de Goede  * @count: number of register, value pairs in the list
42ad85094bSMauro Carvalho Chehab  */
43ad85094bSMauro Carvalho Chehab static int gc0310_write_reg_array(struct i2c_client *client,
44bfe06aeeSHans de Goede 				  const struct gc0310_reg *reglist, int count)
45ad85094bSMauro Carvalho Chehab {
46bfe06aeeSHans de Goede 	int i, err;
47ad85094bSMauro Carvalho Chehab 
48bfe06aeeSHans de Goede 	for (i = 0; i < count; i++) {
49*65e5ef2fSHans de Goede 		err = i2c_smbus_write_byte_data(client, reglist[i].reg, reglist[i].val);
50*65e5ef2fSHans de Goede 		if (err) {
51*65e5ef2fSHans de Goede 			dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d",
52*65e5ef2fSHans de Goede 				reglist[i].val, reglist[i].reg, err);
53ad85094bSMauro Carvalho Chehab 			return err;
54ad85094bSMauro Carvalho Chehab 		}
55*65e5ef2fSHans de Goede 	}
56ad85094bSMauro Carvalho Chehab 
57e1a4b3a7SHans de Goede 	return 0;
58ad85094bSMauro Carvalho Chehab }
59bdfe0bebSMauro Carvalho Chehab 
60ad85094bSMauro Carvalho Chehab static int gc0310_set_gain(struct v4l2_subdev *sd, int gain)
61ad85094bSMauro Carvalho Chehab 
62ad85094bSMauro Carvalho Chehab {
63ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
64ad85094bSMauro Carvalho Chehab 	int ret;
65ad85094bSMauro Carvalho Chehab 	u8 again, dgain;
66ad85094bSMauro Carvalho Chehab 
67ad85094bSMauro Carvalho Chehab 	if (gain < 0x20)
68ad85094bSMauro Carvalho Chehab 		gain = 0x20;
69ad85094bSMauro Carvalho Chehab 	if (gain > 0x80)
70ad85094bSMauro Carvalho Chehab 		gain = 0x80;
71ad85094bSMauro Carvalho Chehab 
72ad85094bSMauro Carvalho Chehab 	if (gain >= 0x20 && gain < 0x40) {
73ad85094bSMauro Carvalho Chehab 		again = 0x0; /* sqrt(2) */
74ad85094bSMauro Carvalho Chehab 		dgain = gain;
75ad85094bSMauro Carvalho Chehab 	} else {
76ad85094bSMauro Carvalho Chehab 		again = 0x2; /* 2 * sqrt(2) */
77ad85094bSMauro Carvalho Chehab 		dgain = gain / 2;
78ad85094bSMauro Carvalho Chehab 	}
79ad85094bSMauro Carvalho Chehab 
802dfc978aSDeepak R Varma 	dev_dbg(&client->dev, "gain=0x%x again=0x%x dgain=0x%x\n", gain, again, dgain);
81ad85094bSMauro Carvalho Chehab 
82ad85094bSMauro Carvalho Chehab 	/* set analog gain */
83*65e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_AGC_ADJ, again);
84ad85094bSMauro Carvalho Chehab 	if (ret)
85ad85094bSMauro Carvalho Chehab 		return ret;
86ad85094bSMauro Carvalho Chehab 
87ad85094bSMauro Carvalho Chehab 	/* set digital gain */
88*65e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_DGC_ADJ, dgain);
89ad85094bSMauro Carvalho Chehab 	if (ret)
90ad85094bSMauro Carvalho Chehab 		return ret;
91ad85094bSMauro Carvalho Chehab 
92ad85094bSMauro Carvalho Chehab 	return 0;
93ad85094bSMauro Carvalho Chehab }
94ad85094bSMauro Carvalho Chehab 
95ad85094bSMauro Carvalho Chehab static int __gc0310_set_exposure(struct v4l2_subdev *sd, int coarse_itg,
96ad85094bSMauro Carvalho Chehab 				 int gain, int digitgain)
97ad85094bSMauro Carvalho Chehab 
98ad85094bSMauro Carvalho Chehab {
99ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
100ad85094bSMauro Carvalho Chehab 	int ret;
101ad85094bSMauro Carvalho Chehab 
1022dfc978aSDeepak R Varma 	dev_dbg(&client->dev, "coarse_itg=%d gain=%d digitgain=%d\n", coarse_itg, gain, digitgain);
103ad85094bSMauro Carvalho Chehab 
104ad85094bSMauro Carvalho Chehab 	/* set exposure */
105*65e5ef2fSHans de Goede 	ret = i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, coarse_itg);
106ad85094bSMauro Carvalho Chehab 	if (ret)
107ad85094bSMauro Carvalho Chehab 		return ret;
108ad85094bSMauro Carvalho Chehab 
109ad85094bSMauro Carvalho Chehab 	ret = gc0310_set_gain(sd, gain);
110ad85094bSMauro Carvalho Chehab 	if (ret)
111ad85094bSMauro Carvalho Chehab 		return ret;
112ad85094bSMauro Carvalho Chehab 
113ad85094bSMauro Carvalho Chehab 	return ret;
114ad85094bSMauro Carvalho Chehab }
115ad85094bSMauro Carvalho Chehab 
116ad85094bSMauro Carvalho Chehab static int gc0310_set_exposure(struct v4l2_subdev *sd, int exposure,
117ad85094bSMauro Carvalho Chehab 			       int gain, int digitgain)
118ad85094bSMauro Carvalho Chehab {
119ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
120ad85094bSMauro Carvalho Chehab 	int ret;
121ad85094bSMauro Carvalho Chehab 
122ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
123ad85094bSMauro Carvalho Chehab 	ret = __gc0310_set_exposure(sd, exposure, gain, digitgain);
124ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
125ad85094bSMauro Carvalho Chehab 
126ad85094bSMauro Carvalho Chehab 	return ret;
127ad85094bSMauro Carvalho Chehab }
128ad85094bSMauro Carvalho Chehab 
129ad85094bSMauro Carvalho Chehab static long gc0310_s_exposure(struct v4l2_subdev *sd,
130ad85094bSMauro Carvalho Chehab 			      struct atomisp_exposure *exposure)
131ad85094bSMauro Carvalho Chehab {
132ad85094bSMauro Carvalho Chehab 	int exp = exposure->integration_time[0];
133ad85094bSMauro Carvalho Chehab 	int gain = exposure->gain[0];
134ad85094bSMauro Carvalho Chehab 	int digitgain = exposure->gain[1];
135ad85094bSMauro Carvalho Chehab 
136ad85094bSMauro Carvalho Chehab 	/* we should not accept the invalid value below. */
137ad85094bSMauro Carvalho Chehab 	if (gain == 0) {
138ad85094bSMauro Carvalho Chehab 		struct i2c_client *client = v4l2_get_subdevdata(sd);
139bdfe0bebSMauro Carvalho Chehab 
140ad85094bSMauro Carvalho Chehab 		v4l2_err(client, "%s: invalid value\n", __func__);
141ad85094bSMauro Carvalho Chehab 		return -EINVAL;
142ad85094bSMauro Carvalho Chehab 	}
143ad85094bSMauro Carvalho Chehab 
144ad85094bSMauro Carvalho Chehab 	return gc0310_set_exposure(sd, exp, gain, digitgain);
145ad85094bSMauro Carvalho Chehab }
146ad85094bSMauro Carvalho Chehab 
147ad85094bSMauro Carvalho Chehab /* TO DO */
148ad85094bSMauro Carvalho Chehab static int gc0310_v_flip(struct v4l2_subdev *sd, s32 value)
149ad85094bSMauro Carvalho Chehab {
150ad85094bSMauro Carvalho Chehab 	return 0;
151ad85094bSMauro Carvalho Chehab }
152ad85094bSMauro Carvalho Chehab 
153ad85094bSMauro Carvalho Chehab /* TO DO */
154ad85094bSMauro Carvalho Chehab static int gc0310_h_flip(struct v4l2_subdev *sd, s32 value)
155ad85094bSMauro Carvalho Chehab {
156ad85094bSMauro Carvalho Chehab 	return 0;
157ad85094bSMauro Carvalho Chehab }
158ad85094bSMauro Carvalho Chehab 
159ad85094bSMauro Carvalho Chehab static long gc0310_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
160ad85094bSMauro Carvalho Chehab {
161ad85094bSMauro Carvalho Chehab 	switch (cmd) {
162ad85094bSMauro Carvalho Chehab 	case ATOMISP_IOC_S_EXPOSURE:
163ad85094bSMauro Carvalho Chehab 		return gc0310_s_exposure(sd, arg);
164ad85094bSMauro Carvalho Chehab 	default:
165ad85094bSMauro Carvalho Chehab 		return -EINVAL;
166ad85094bSMauro Carvalho Chehab 	}
167ad85094bSMauro Carvalho Chehab 	return 0;
168ad85094bSMauro Carvalho Chehab }
169ad85094bSMauro Carvalho Chehab 
170ad85094bSMauro Carvalho Chehab /* This returns the exposure time being used. This should only be used
171ad85094bSMauro Carvalho Chehab  * for filling in EXIF data, not for actual image processing.
172ad85094bSMauro Carvalho Chehab  */
173ad85094bSMauro Carvalho Chehab static int gc0310_q_exposure(struct v4l2_subdev *sd, s32 *value)
174ad85094bSMauro Carvalho Chehab {
175ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
176ad85094bSMauro Carvalho Chehab 	int ret;
177ad85094bSMauro Carvalho Chehab 
178ad85094bSMauro Carvalho Chehab 	/* get exposure */
179*65e5ef2fSHans de Goede 	ret = i2c_smbus_read_word_swapped(client, GC0310_AEC_PK_EXPO_H);
180*65e5ef2fSHans de Goede 	if (ret < 0)
181ad85094bSMauro Carvalho Chehab 		return ret;
182*65e5ef2fSHans de Goede 
183*65e5ef2fSHans de Goede 	*value = ret;
184*65e5ef2fSHans de Goede 	return 0;
185ad85094bSMauro Carvalho Chehab }
186ad85094bSMauro Carvalho Chehab 
187ad85094bSMauro Carvalho Chehab static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl)
188ad85094bSMauro Carvalho Chehab {
189ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev =
190ad85094bSMauro Carvalho Chehab 	    container_of(ctrl->handler, struct gc0310_device, ctrl_handler);
191ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
192ad85094bSMauro Carvalho Chehab 	int ret = 0;
193ad85094bSMauro Carvalho Chehab 
194ad85094bSMauro Carvalho Chehab 	switch (ctrl->id) {
195ad85094bSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
196ad85094bSMauro Carvalho Chehab 		dev_dbg(&client->dev, "%s: CID_VFLIP:%d.\n",
197ad85094bSMauro Carvalho Chehab 			__func__, ctrl->val);
198ad85094bSMauro Carvalho Chehab 		ret = gc0310_v_flip(&dev->sd, ctrl->val);
199ad85094bSMauro Carvalho Chehab 		break;
200ad85094bSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
201ad85094bSMauro Carvalho Chehab 		dev_dbg(&client->dev, "%s: CID_HFLIP:%d.\n",
202ad85094bSMauro Carvalho Chehab 			__func__, ctrl->val);
203ad85094bSMauro Carvalho Chehab 		ret = gc0310_h_flip(&dev->sd, ctrl->val);
204ad85094bSMauro Carvalho Chehab 		break;
205ad85094bSMauro Carvalho Chehab 	default:
206ad85094bSMauro Carvalho Chehab 		ret = -EINVAL;
207ad85094bSMauro Carvalho Chehab 	}
208ad85094bSMauro Carvalho Chehab 	return ret;
209ad85094bSMauro Carvalho Chehab }
210ad85094bSMauro Carvalho Chehab 
211ad85094bSMauro Carvalho Chehab static int gc0310_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
212ad85094bSMauro Carvalho Chehab {
213ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev =
214ad85094bSMauro Carvalho Chehab 	    container_of(ctrl->handler, struct gc0310_device, ctrl_handler);
215ad85094bSMauro Carvalho Chehab 	int ret = 0;
216ad85094bSMauro Carvalho Chehab 
217ad85094bSMauro Carvalho Chehab 	switch (ctrl->id) {
218ad85094bSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE_ABSOLUTE:
219ad85094bSMauro Carvalho Chehab 		ret = gc0310_q_exposure(&dev->sd, &ctrl->val);
220ad85094bSMauro Carvalho Chehab 		break;
221ad85094bSMauro Carvalho Chehab 	default:
222ad85094bSMauro Carvalho Chehab 		ret = -EINVAL;
223ad85094bSMauro Carvalho Chehab 	}
224ad85094bSMauro Carvalho Chehab 
225ad85094bSMauro Carvalho Chehab 	return ret;
226ad85094bSMauro Carvalho Chehab }
227ad85094bSMauro Carvalho Chehab 
228ad85094bSMauro Carvalho Chehab static const struct v4l2_ctrl_ops ctrl_ops = {
229ad85094bSMauro Carvalho Chehab 	.s_ctrl = gc0310_s_ctrl,
230ad85094bSMauro Carvalho Chehab 	.g_volatile_ctrl = gc0310_g_volatile_ctrl
231ad85094bSMauro Carvalho Chehab };
232ad85094bSMauro Carvalho Chehab 
233ad85094bSMauro Carvalho Chehab static const struct v4l2_ctrl_config gc0310_controls[] = {
234ad85094bSMauro Carvalho Chehab 	{
235ad85094bSMauro Carvalho Chehab 		.ops = &ctrl_ops,
236ad85094bSMauro Carvalho Chehab 		.id = V4L2_CID_EXPOSURE_ABSOLUTE,
237ad85094bSMauro Carvalho Chehab 		.type = V4L2_CTRL_TYPE_INTEGER,
238ad85094bSMauro Carvalho Chehab 		.name = "exposure",
239ad85094bSMauro Carvalho Chehab 		.min = 0x0,
240ad85094bSMauro Carvalho Chehab 		.max = 0xffff,
241ad85094bSMauro Carvalho Chehab 		.step = 0x01,
242ad85094bSMauro Carvalho Chehab 		.def = 0x00,
243ad85094bSMauro Carvalho Chehab 		.flags = 0,
244ad85094bSMauro Carvalho Chehab 	},
245ad85094bSMauro Carvalho Chehab 	{
246ad85094bSMauro Carvalho Chehab 		.ops = &ctrl_ops,
247ad85094bSMauro Carvalho Chehab 		.id = V4L2_CID_VFLIP,
248ad85094bSMauro Carvalho Chehab 		.type = V4L2_CTRL_TYPE_BOOLEAN,
249ad85094bSMauro Carvalho Chehab 		.name = "Flip",
250ad85094bSMauro Carvalho Chehab 		.min = 0,
251ad85094bSMauro Carvalho Chehab 		.max = 1,
252ad85094bSMauro Carvalho Chehab 		.step = 1,
253ad85094bSMauro Carvalho Chehab 		.def = 0,
254ad85094bSMauro Carvalho Chehab 	},
255ad85094bSMauro Carvalho Chehab 	{
256ad85094bSMauro Carvalho Chehab 		.ops = &ctrl_ops,
257ad85094bSMauro Carvalho Chehab 		.id = V4L2_CID_HFLIP,
258ad85094bSMauro Carvalho Chehab 		.type = V4L2_CTRL_TYPE_BOOLEAN,
259ad85094bSMauro Carvalho Chehab 		.name = "Mirror",
260ad85094bSMauro Carvalho Chehab 		.min = 0,
261ad85094bSMauro Carvalho Chehab 		.max = 1,
262ad85094bSMauro Carvalho Chehab 		.step = 1,
263ad85094bSMauro Carvalho Chehab 		.def = 0,
264ad85094bSMauro Carvalho Chehab 	},
265ad85094bSMauro Carvalho Chehab };
266ad85094bSMauro Carvalho Chehab 
267ad85094bSMauro Carvalho Chehab static int gc0310_init(struct v4l2_subdev *sd)
268ad85094bSMauro Carvalho Chehab {
269ad85094bSMauro Carvalho Chehab 	int ret;
270ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
271ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
272ad85094bSMauro Carvalho Chehab 
273ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
274ad85094bSMauro Carvalho Chehab 
2754636a85cSMauro Carvalho Chehab 	/* set initial registers */
276bfe06aeeSHans de Goede 	ret = gc0310_write_reg_array(client, gc0310_reset_register,
277bfe06aeeSHans de Goede 				     ARRAY_SIZE(gc0310_reset_register));
278ad85094bSMauro Carvalho Chehab 
279ad85094bSMauro Carvalho Chehab 	/* restore settings */
280ad85094bSMauro Carvalho Chehab 	gc0310_res = gc0310_res_preview;
281ad85094bSMauro Carvalho Chehab 	N_RES = N_RES_PREVIEW;
282ad85094bSMauro Carvalho Chehab 
283ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
284ad85094bSMauro Carvalho Chehab 
285ad85094bSMauro Carvalho Chehab 	return ret;
286ad85094bSMauro Carvalho Chehab }
287ad85094bSMauro Carvalho Chehab 
288ad85094bSMauro Carvalho Chehab static int power_ctrl(struct v4l2_subdev *sd, bool flag)
289ad85094bSMauro Carvalho Chehab {
290ad85094bSMauro Carvalho Chehab 	int ret = 0;
291ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
292bdfe0bebSMauro Carvalho Chehab 
293ad85094bSMauro Carvalho Chehab 	if (!dev || !dev->platform_data)
294ad85094bSMauro Carvalho Chehab 		return -ENODEV;
295ad85094bSMauro Carvalho Chehab 
296ad85094bSMauro Carvalho Chehab 	if (flag) {
297ad85094bSMauro Carvalho Chehab 		/* The upstream module driver (written to Crystal
298ad85094bSMauro Carvalho Chehab 		 * Cove) had this logic to pulse the rails low first.
299ad85094bSMauro Carvalho Chehab 		 * This appears to break things on the MRD7 with the
300ad85094bSMauro Carvalho Chehab 		 * X-Powers PMIC...
301ad85094bSMauro Carvalho Chehab 		 *
302ad85094bSMauro Carvalho Chehab 		 *     ret = dev->platform_data->v1p8_ctrl(sd, 0);
303ad85094bSMauro Carvalho Chehab 		 *     ret |= dev->platform_data->v2p8_ctrl(sd, 0);
304ad85094bSMauro Carvalho Chehab 		 *     mdelay(50);
305ad85094bSMauro Carvalho Chehab 		 */
306ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v1p8_ctrl(sd, 1);
307ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v2p8_ctrl(sd, 1);
308ad85094bSMauro Carvalho Chehab 		usleep_range(10000, 15000);
309ad85094bSMauro Carvalho Chehab 	}
310ad85094bSMauro Carvalho Chehab 
311ad85094bSMauro Carvalho Chehab 	if (!flag || ret) {
312ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v1p8_ctrl(sd, 0);
313ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v2p8_ctrl(sd, 0);
314ad85094bSMauro Carvalho Chehab 	}
315ad85094bSMauro Carvalho Chehab 	return ret;
316ad85094bSMauro Carvalho Chehab }
317ad85094bSMauro Carvalho Chehab 
318ad85094bSMauro Carvalho Chehab static int gpio_ctrl(struct v4l2_subdev *sd, bool flag)
319ad85094bSMauro Carvalho Chehab {
320ad85094bSMauro Carvalho Chehab 	int ret;
321ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
322ad85094bSMauro Carvalho Chehab 
323ad85094bSMauro Carvalho Chehab 	if (!dev || !dev->platform_data)
324ad85094bSMauro Carvalho Chehab 		return -ENODEV;
325ad85094bSMauro Carvalho Chehab 
326ad85094bSMauro Carvalho Chehab 	/* GPIO0 == "reset" (active low), GPIO1 == "power down" */
327ad85094bSMauro Carvalho Chehab 	if (flag) {
328ad85094bSMauro Carvalho Chehab 		/* Pulse reset, then release power down */
329ad85094bSMauro Carvalho Chehab 		ret = dev->platform_data->gpio0_ctrl(sd, 0);
330ad85094bSMauro Carvalho Chehab 		usleep_range(5000, 10000);
331ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->gpio0_ctrl(sd, 1);
332ad85094bSMauro Carvalho Chehab 		usleep_range(10000, 15000);
333ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->gpio1_ctrl(sd, 0);
334ad85094bSMauro Carvalho Chehab 		usleep_range(10000, 15000);
335ad85094bSMauro Carvalho Chehab 	} else {
336ad85094bSMauro Carvalho Chehab 		ret = dev->platform_data->gpio1_ctrl(sd, 1);
337ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->gpio0_ctrl(sd, 0);
338ad85094bSMauro Carvalho Chehab 	}
339ad85094bSMauro Carvalho Chehab 	return ret;
340ad85094bSMauro Carvalho Chehab }
341ad85094bSMauro Carvalho Chehab 
342ad85094bSMauro Carvalho Chehab static int power_up(struct v4l2_subdev *sd)
343ad85094bSMauro Carvalho Chehab {
344ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
345ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
346ad85094bSMauro Carvalho Chehab 	int ret;
347ad85094bSMauro Carvalho Chehab 
348ad85094bSMauro Carvalho Chehab 	if (!dev->platform_data) {
349ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev,
350ad85094bSMauro Carvalho Chehab 			"no camera_sensor_platform_data");
351ad85094bSMauro Carvalho Chehab 		return -ENODEV;
352ad85094bSMauro Carvalho Chehab 	}
353ad85094bSMauro Carvalho Chehab 
354c5fafbadSHans de Goede 	if (dev->power_on)
355c5fafbadSHans de Goede 		return 0; /* Already on */
356c5fafbadSHans de Goede 
357ad85094bSMauro Carvalho Chehab 	/* power control */
358ad85094bSMauro Carvalho Chehab 	ret = power_ctrl(sd, 1);
359ad85094bSMauro Carvalho Chehab 	if (ret)
360ad85094bSMauro Carvalho Chehab 		goto fail_power;
361ad85094bSMauro Carvalho Chehab 
362ad85094bSMauro Carvalho Chehab 	/* flis clock control */
363ad85094bSMauro Carvalho Chehab 	ret = dev->platform_data->flisclk_ctrl(sd, 1);
364ad85094bSMauro Carvalho Chehab 	if (ret)
365ad85094bSMauro Carvalho Chehab 		goto fail_clk;
366ad85094bSMauro Carvalho Chehab 
367ad85094bSMauro Carvalho Chehab 	/* gpio ctrl */
368ad85094bSMauro Carvalho Chehab 	ret = gpio_ctrl(sd, 1);
369ad85094bSMauro Carvalho Chehab 	if (ret) {
370ad85094bSMauro Carvalho Chehab 		ret = gpio_ctrl(sd, 1);
371ad85094bSMauro Carvalho Chehab 		if (ret)
372ad85094bSMauro Carvalho Chehab 			goto fail_gpio;
373ad85094bSMauro Carvalho Chehab 	}
374ad85094bSMauro Carvalho Chehab 
375ad85094bSMauro Carvalho Chehab 	msleep(100);
376ad85094bSMauro Carvalho Chehab 
377c5fafbadSHans de Goede 	dev->power_on = true;
378ad85094bSMauro Carvalho Chehab 	return 0;
379ad85094bSMauro Carvalho Chehab 
380ad85094bSMauro Carvalho Chehab fail_gpio:
381ad85094bSMauro Carvalho Chehab 	dev->platform_data->flisclk_ctrl(sd, 0);
382ad85094bSMauro Carvalho Chehab fail_clk:
383ad85094bSMauro Carvalho Chehab 	power_ctrl(sd, 0);
384ad85094bSMauro Carvalho Chehab fail_power:
385ad85094bSMauro Carvalho Chehab 	dev_err(&client->dev, "sensor power-up failed\n");
386ad85094bSMauro Carvalho Chehab 
387ad85094bSMauro Carvalho Chehab 	return ret;
388ad85094bSMauro Carvalho Chehab }
389ad85094bSMauro Carvalho Chehab 
390ad85094bSMauro Carvalho Chehab static int power_down(struct v4l2_subdev *sd)
391ad85094bSMauro Carvalho Chehab {
392ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
393ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
394ad85094bSMauro Carvalho Chehab 	int ret = 0;
395ad85094bSMauro Carvalho Chehab 
396ad85094bSMauro Carvalho Chehab 	if (!dev->platform_data) {
397ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev,
398ad85094bSMauro Carvalho Chehab 			"no camera_sensor_platform_data");
399ad85094bSMauro Carvalho Chehab 		return -ENODEV;
400ad85094bSMauro Carvalho Chehab 	}
401ad85094bSMauro Carvalho Chehab 
402c5fafbadSHans de Goede 	if (!dev->power_on)
403c5fafbadSHans de Goede 		return 0; /* Already off */
404c5fafbadSHans de Goede 
405ad85094bSMauro Carvalho Chehab 	/* gpio ctrl */
406ad85094bSMauro Carvalho Chehab 	ret = gpio_ctrl(sd, 0);
407ad85094bSMauro Carvalho Chehab 	if (ret) {
408ad85094bSMauro Carvalho Chehab 		ret = gpio_ctrl(sd, 0);
409ad85094bSMauro Carvalho Chehab 		if (ret)
410ad85094bSMauro Carvalho Chehab 			dev_err(&client->dev, "gpio failed 2\n");
411ad85094bSMauro Carvalho Chehab 	}
412ad85094bSMauro Carvalho Chehab 
413ad85094bSMauro Carvalho Chehab 	ret = dev->platform_data->flisclk_ctrl(sd, 0);
414ad85094bSMauro Carvalho Chehab 	if (ret)
415ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "flisclk failed\n");
416ad85094bSMauro Carvalho Chehab 
417ad85094bSMauro Carvalho Chehab 	/* power control */
418ad85094bSMauro Carvalho Chehab 	ret = power_ctrl(sd, 0);
419ad85094bSMauro Carvalho Chehab 	if (ret)
420ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "vprog failed.\n");
421ad85094bSMauro Carvalho Chehab 
422c5fafbadSHans de Goede 	dev->power_on = false;
423ad85094bSMauro Carvalho Chehab 	return ret;
424ad85094bSMauro Carvalho Chehab }
425ad85094bSMauro Carvalho Chehab 
426ad85094bSMauro Carvalho Chehab static int gc0310_s_power(struct v4l2_subdev *sd, int on)
427ad85094bSMauro Carvalho Chehab {
428ad85094bSMauro Carvalho Chehab 	int ret;
429bdfe0bebSMauro Carvalho Chehab 
430ad85094bSMauro Carvalho Chehab 	if (on == 0)
431ad85094bSMauro Carvalho Chehab 		return power_down(sd);
4324658e1dbSLeonid Kushnir 
433dfe59c78SLeonid Kushnir 	ret = power_up(sd);
434dfe59c78SLeonid Kushnir 	if (ret)
435ad85094bSMauro Carvalho Chehab 		return ret;
436dfe59c78SLeonid Kushnir 
437dfe59c78SLeonid Kushnir 	return gc0310_init(sd);
438ad85094bSMauro Carvalho Chehab }
439ad85094bSMauro Carvalho Chehab 
440ad85094bSMauro Carvalho Chehab /* TODO: remove it. */
441ad85094bSMauro Carvalho Chehab static int startup(struct v4l2_subdev *sd)
442ad85094bSMauro Carvalho Chehab {
443ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
444ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
445ad85094bSMauro Carvalho Chehab 	int ret = 0;
446ad85094bSMauro Carvalho Chehab 
447bfe06aeeSHans de Goede 	ret = gc0310_write_reg_array(client, dev->res->regs, dev->res->reg_count);
448ad85094bSMauro Carvalho Chehab 	if (ret) {
449ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 write register err.\n");
450ad85094bSMauro Carvalho Chehab 		return ret;
451ad85094bSMauro Carvalho Chehab 	}
452ad85094bSMauro Carvalho Chehab 
453ad85094bSMauro Carvalho Chehab 	return ret;
454ad85094bSMauro Carvalho Chehab }
455ad85094bSMauro Carvalho Chehab 
456ad85094bSMauro Carvalho Chehab static int gc0310_set_fmt(struct v4l2_subdev *sd,
4570d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
458ad85094bSMauro Carvalho Chehab 			  struct v4l2_subdev_format *format)
459ad85094bSMauro Carvalho Chehab {
460ad85094bSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *fmt = &format->format;
461ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
462ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
463ad85094bSMauro Carvalho Chehab 	struct camera_mipi_info *gc0310_info = NULL;
464e3b14bf8SMauro Carvalho Chehab 	struct gc0310_resolution *res;
465ad85094bSMauro Carvalho Chehab 	int ret = 0;
466bdfe0bebSMauro Carvalho Chehab 
467ad85094bSMauro Carvalho Chehab 	if (format->pad)
468ad85094bSMauro Carvalho Chehab 		return -EINVAL;
469ad85094bSMauro Carvalho Chehab 
470ad85094bSMauro Carvalho Chehab 	if (!fmt)
471ad85094bSMauro Carvalho Chehab 		return -EINVAL;
472ad85094bSMauro Carvalho Chehab 
473ad85094bSMauro Carvalho Chehab 	gc0310_info = v4l2_get_subdev_hostdata(sd);
474ad85094bSMauro Carvalho Chehab 	if (!gc0310_info)
475ad85094bSMauro Carvalho Chehab 		return -EINVAL;
476ad85094bSMauro Carvalho Chehab 
477ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
478ad85094bSMauro Carvalho Chehab 
479e3b14bf8SMauro Carvalho Chehab 	res = v4l2_find_nearest_size(gc0310_res_preview,
480e3b14bf8SMauro Carvalho Chehab 				     ARRAY_SIZE(gc0310_res_preview), width,
481e3b14bf8SMauro Carvalho Chehab 				     height, fmt->width, fmt->height);
482e3b14bf8SMauro Carvalho Chehab 	if (!res)
483e3b14bf8SMauro Carvalho Chehab 		res = &gc0310_res_preview[N_RES - 1];
484e3b14bf8SMauro Carvalho Chehab 
485e3b14bf8SMauro Carvalho Chehab 	fmt->width = res->width;
486e3b14bf8SMauro Carvalho Chehab 	fmt->height = res->height;
487e3b14bf8SMauro Carvalho Chehab 	dev->res = res;
488e3b14bf8SMauro Carvalho Chehab 
489ad85094bSMauro Carvalho Chehab 	fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
490ad85094bSMauro Carvalho Chehab 
491ad85094bSMauro Carvalho Chehab 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
4920d346d2aSTomi Valkeinen 		sd_state->pads->try_fmt = *fmt;
493ad85094bSMauro Carvalho Chehab 		mutex_unlock(&dev->input_lock);
494ad85094bSMauro Carvalho Chehab 		return 0;
495ad85094bSMauro Carvalho Chehab 	}
496ad85094bSMauro Carvalho Chehab 
497c5fafbadSHans de Goede 	/* s_power has not been called yet for std v4l2 clients (camorama) */
498c5fafbadSHans de Goede 	power_up(sd);
499c5fafbadSHans de Goede 
5002dfc978aSDeepak R Varma 	dev_dbg(&client->dev, "%s: before gc0310_write_reg_array %s\n",
501e3b14bf8SMauro Carvalho Chehab 		__func__, dev->res->desc);
502ad85094bSMauro Carvalho Chehab 	ret = startup(sd);
503ad85094bSMauro Carvalho Chehab 	if (ret) {
504ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 startup err\n");
505ad85094bSMauro Carvalho Chehab 		goto err;
506ad85094bSMauro Carvalho Chehab 	}
507ad85094bSMauro Carvalho Chehab 
508ad85094bSMauro Carvalho Chehab err:
509ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
510ad85094bSMauro Carvalho Chehab 	return ret;
511ad85094bSMauro Carvalho Chehab }
512ad85094bSMauro Carvalho Chehab 
513ad85094bSMauro Carvalho Chehab static int gc0310_get_fmt(struct v4l2_subdev *sd,
5140d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
515ad85094bSMauro Carvalho Chehab 			  struct v4l2_subdev_format *format)
516ad85094bSMauro Carvalho Chehab {
517ad85094bSMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *fmt = &format->format;
518ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
519ad85094bSMauro Carvalho Chehab 
520ad85094bSMauro Carvalho Chehab 	if (format->pad)
521ad85094bSMauro Carvalho Chehab 		return -EINVAL;
522ad85094bSMauro Carvalho Chehab 
523ad85094bSMauro Carvalho Chehab 	if (!fmt)
524ad85094bSMauro Carvalho Chehab 		return -EINVAL;
525ad85094bSMauro Carvalho Chehab 
526e3b14bf8SMauro Carvalho Chehab 	fmt->width = dev->res->width;
527e3b14bf8SMauro Carvalho Chehab 	fmt->height = dev->res->height;
528ad85094bSMauro Carvalho Chehab 	fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
529ad85094bSMauro Carvalho Chehab 
530ad85094bSMauro Carvalho Chehab 	return 0;
531ad85094bSMauro Carvalho Chehab }
532ad85094bSMauro Carvalho Chehab 
533ad85094bSMauro Carvalho Chehab static int gc0310_detect(struct i2c_client *client)
534ad85094bSMauro Carvalho Chehab {
535ad85094bSMauro Carvalho Chehab 	struct i2c_adapter *adapter = client->adapter;
536ad85094bSMauro Carvalho Chehab 	int ret;
537ad85094bSMauro Carvalho Chehab 
538ad85094bSMauro Carvalho Chehab 	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
539ad85094bSMauro Carvalho Chehab 		return -ENODEV;
540ad85094bSMauro Carvalho Chehab 
541*65e5ef2fSHans de Goede 	ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H);
542*65e5ef2fSHans de Goede 	if (ret < 0) {
543*65e5ef2fSHans de Goede 		dev_err(&client->dev, "read sensor_id failed: %d\n", ret);
544ad85094bSMauro Carvalho Chehab 		return -ENODEV;
545ad85094bSMauro Carvalho Chehab 	}
546ad85094bSMauro Carvalho Chehab 
547*65e5ef2fSHans de Goede 	dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret);
548*65e5ef2fSHans de Goede 
549*65e5ef2fSHans de Goede 	if (ret != GC0310_ID) {
550*65e5ef2fSHans de Goede 		dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n",
551*65e5ef2fSHans de Goede 			ret, GC0310_ID);
552ad85094bSMauro Carvalho Chehab 		return -ENODEV;
553ad85094bSMauro Carvalho Chehab 	}
554ad85094bSMauro Carvalho Chehab 
555ad85094bSMauro Carvalho Chehab 	dev_dbg(&client->dev, "detect gc0310 success\n");
556ad85094bSMauro Carvalho Chehab 
557ad85094bSMauro Carvalho Chehab 	return 0;
558ad85094bSMauro Carvalho Chehab }
559ad85094bSMauro Carvalho Chehab 
560ad85094bSMauro Carvalho Chehab static int gc0310_s_stream(struct v4l2_subdev *sd, int enable)
561ad85094bSMauro Carvalho Chehab {
562ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
563ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
564ad85094bSMauro Carvalho Chehab 	int ret;
565ad85094bSMauro Carvalho Chehab 
5662dfc978aSDeepak R Varma 	dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable);
567ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
568ad85094bSMauro Carvalho Chehab 
569ad85094bSMauro Carvalho Chehab 	if (enable) {
570ad85094bSMauro Carvalho Chehab 		/* enable per frame MIPI and sensor ctrl reset  */
571*65e5ef2fSHans de Goede 		ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30);
572ad85094bSMauro Carvalho Chehab 		if (ret) {
573ad85094bSMauro Carvalho Chehab 			mutex_unlock(&dev->input_lock);
574ad85094bSMauro Carvalho Chehab 			return ret;
575ad85094bSMauro Carvalho Chehab 		}
576ad85094bSMauro Carvalho Chehab 	}
577ad85094bSMauro Carvalho Chehab 
578*65e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3);
579ad85094bSMauro Carvalho Chehab 	if (ret) {
580ad85094bSMauro Carvalho Chehab 		mutex_unlock(&dev->input_lock);
581ad85094bSMauro Carvalho Chehab 		return ret;
582ad85094bSMauro Carvalho Chehab 	}
583ad85094bSMauro Carvalho Chehab 
584*65e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM,
585*65e5ef2fSHans de Goede 					enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING);
586ad85094bSMauro Carvalho Chehab 	if (ret) {
587ad85094bSMauro Carvalho Chehab 		mutex_unlock(&dev->input_lock);
588ad85094bSMauro Carvalho Chehab 		return ret;
589ad85094bSMauro Carvalho Chehab 	}
590ad85094bSMauro Carvalho Chehab 
591*65e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0);
592ad85094bSMauro Carvalho Chehab 	if (ret) {
593ad85094bSMauro Carvalho Chehab 		mutex_unlock(&dev->input_lock);
594ad85094bSMauro Carvalho Chehab 		return ret;
595ad85094bSMauro Carvalho Chehab 	}
596ad85094bSMauro Carvalho Chehab 
597ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
598ad85094bSMauro Carvalho Chehab 	return ret;
599ad85094bSMauro Carvalho Chehab }
600ad85094bSMauro Carvalho Chehab 
601ad85094bSMauro Carvalho Chehab static int gc0310_s_config(struct v4l2_subdev *sd,
602ad85094bSMauro Carvalho Chehab 			   int irq, void *platform_data)
603ad85094bSMauro Carvalho Chehab {
604ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
605ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
606ad85094bSMauro Carvalho Chehab 	int ret = 0;
607ad85094bSMauro Carvalho Chehab 
608ad85094bSMauro Carvalho Chehab 	if (!platform_data)
609ad85094bSMauro Carvalho Chehab 		return -ENODEV;
610ad85094bSMauro Carvalho Chehab 
611ad85094bSMauro Carvalho Chehab 	dev->platform_data =
612ad85094bSMauro Carvalho Chehab 	    (struct camera_sensor_platform_data *)platform_data;
613ad85094bSMauro Carvalho Chehab 
614ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
615ad85094bSMauro Carvalho Chehab 	/* power off the module, then power on it in future
616ad85094bSMauro Carvalho Chehab 	 * as first power on by board may not fulfill the
617ad85094bSMauro Carvalho Chehab 	 * power on sequqence needed by the module
618ad85094bSMauro Carvalho Chehab 	 */
619c5fafbadSHans de Goede 	dev->power_on = true; /* force power_down() to run */
620ad85094bSMauro Carvalho Chehab 	ret = power_down(sd);
621ad85094bSMauro Carvalho Chehab 	if (ret) {
622ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 power-off err.\n");
623ad85094bSMauro Carvalho Chehab 		goto fail_power_off;
624ad85094bSMauro Carvalho Chehab 	}
625ad85094bSMauro Carvalho Chehab 
626ad85094bSMauro Carvalho Chehab 	ret = power_up(sd);
627ad85094bSMauro Carvalho Chehab 	if (ret) {
628ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 power-up err.\n");
629ad85094bSMauro Carvalho Chehab 		goto fail_power_on;
630ad85094bSMauro Carvalho Chehab 	}
631ad85094bSMauro Carvalho Chehab 
632ad85094bSMauro Carvalho Chehab 	ret = dev->platform_data->csi_cfg(sd, 1);
633ad85094bSMauro Carvalho Chehab 	if (ret)
634ad85094bSMauro Carvalho Chehab 		goto fail_csi_cfg;
635ad85094bSMauro Carvalho Chehab 
636ad85094bSMauro Carvalho Chehab 	/* config & detect sensor */
637ad85094bSMauro Carvalho Chehab 	ret = gc0310_detect(client);
638ad85094bSMauro Carvalho Chehab 	if (ret) {
639ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310_detect err s_config.\n");
640ad85094bSMauro Carvalho Chehab 		goto fail_csi_cfg;
641ad85094bSMauro Carvalho Chehab 	}
642ad85094bSMauro Carvalho Chehab 
643ad85094bSMauro Carvalho Chehab 	/* turn off sensor, after probed */
644ad85094bSMauro Carvalho Chehab 	ret = power_down(sd);
645ad85094bSMauro Carvalho Chehab 	if (ret) {
646ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 power-off err.\n");
647ad85094bSMauro Carvalho Chehab 		goto fail_csi_cfg;
648ad85094bSMauro Carvalho Chehab 	}
649ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
650ad85094bSMauro Carvalho Chehab 
651ad85094bSMauro Carvalho Chehab 	return 0;
652ad85094bSMauro Carvalho Chehab 
653ad85094bSMauro Carvalho Chehab fail_csi_cfg:
654ad85094bSMauro Carvalho Chehab 	dev->platform_data->csi_cfg(sd, 0);
655ad85094bSMauro Carvalho Chehab fail_power_on:
656ad85094bSMauro Carvalho Chehab 	power_down(sd);
657ad85094bSMauro Carvalho Chehab 	dev_err(&client->dev, "sensor power-gating failed\n");
658ad85094bSMauro Carvalho Chehab fail_power_off:
659ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
660ad85094bSMauro Carvalho Chehab 	return ret;
661ad85094bSMauro Carvalho Chehab }
662ad85094bSMauro Carvalho Chehab 
663ad85094bSMauro Carvalho Chehab static int gc0310_g_frame_interval(struct v4l2_subdev *sd,
664ad85094bSMauro Carvalho Chehab 				   struct v4l2_subdev_frame_interval *interval)
665ad85094bSMauro Carvalho Chehab {
666ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
667ad85094bSMauro Carvalho Chehab 
668ad85094bSMauro Carvalho Chehab 	interval->interval.numerator = 1;
669e3b14bf8SMauro Carvalho Chehab 	interval->interval.denominator = dev->res->fps;
670ad85094bSMauro Carvalho Chehab 
671ad85094bSMauro Carvalho Chehab 	return 0;
672ad85094bSMauro Carvalho Chehab }
673ad85094bSMauro Carvalho Chehab 
674ad85094bSMauro Carvalho Chehab static int gc0310_enum_mbus_code(struct v4l2_subdev *sd,
6750d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
676ad85094bSMauro Carvalho Chehab 				 struct v4l2_subdev_mbus_code_enum *code)
677ad85094bSMauro Carvalho Chehab {
678ad85094bSMauro Carvalho Chehab 	if (code->index >= MAX_FMTS)
679ad85094bSMauro Carvalho Chehab 		return -EINVAL;
680ad85094bSMauro Carvalho Chehab 
681ad85094bSMauro Carvalho Chehab 	code->code = MEDIA_BUS_FMT_SGRBG8_1X8;
682ad85094bSMauro Carvalho Chehab 	return 0;
683ad85094bSMauro Carvalho Chehab }
684ad85094bSMauro Carvalho Chehab 
685ad85094bSMauro Carvalho Chehab static int gc0310_enum_frame_size(struct v4l2_subdev *sd,
6860d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
687ad85094bSMauro Carvalho Chehab 				  struct v4l2_subdev_frame_size_enum *fse)
688ad85094bSMauro Carvalho Chehab {
689ad85094bSMauro Carvalho Chehab 	int index = fse->index;
690ad85094bSMauro Carvalho Chehab 
691ad85094bSMauro Carvalho Chehab 	if (index >= N_RES)
692ad85094bSMauro Carvalho Chehab 		return -EINVAL;
693ad85094bSMauro Carvalho Chehab 
694ad85094bSMauro Carvalho Chehab 	fse->min_width = gc0310_res[index].width;
695ad85094bSMauro Carvalho Chehab 	fse->min_height = gc0310_res[index].height;
696ad85094bSMauro Carvalho Chehab 	fse->max_width = gc0310_res[index].width;
697ad85094bSMauro Carvalho Chehab 	fse->max_height = gc0310_res[index].height;
698ad85094bSMauro Carvalho Chehab 
699ad85094bSMauro Carvalho Chehab 	return 0;
700ad85094bSMauro Carvalho Chehab }
701ad85094bSMauro Carvalho Chehab 
702ad85094bSMauro Carvalho Chehab static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
703ad85094bSMauro Carvalho Chehab {
704ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
705ad85094bSMauro Carvalho Chehab 
706ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
707e3b14bf8SMauro Carvalho Chehab 	*frames = dev->res->skip_frames;
708ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
709ad85094bSMauro Carvalho Chehab 
710ad85094bSMauro Carvalho Chehab 	return 0;
711ad85094bSMauro Carvalho Chehab }
712ad85094bSMauro Carvalho Chehab 
713ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = {
714ad85094bSMauro Carvalho Chehab 	.g_skip_frames	= gc0310_g_skip_frames,
715ad85094bSMauro Carvalho Chehab };
716ad85094bSMauro Carvalho Chehab 
717ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops gc0310_video_ops = {
718ad85094bSMauro Carvalho Chehab 	.s_stream = gc0310_s_stream,
719ad85094bSMauro Carvalho Chehab 	.g_frame_interval = gc0310_g_frame_interval,
720ad85094bSMauro Carvalho Chehab };
721ad85094bSMauro Carvalho Chehab 
722ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops gc0310_core_ops = {
723ad85094bSMauro Carvalho Chehab 	.s_power = gc0310_s_power,
724ad85094bSMauro Carvalho Chehab 	.ioctl = gc0310_ioctl,
725ad85094bSMauro Carvalho Chehab };
726ad85094bSMauro Carvalho Chehab 
727ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_pad_ops gc0310_pad_ops = {
728ad85094bSMauro Carvalho Chehab 	.enum_mbus_code = gc0310_enum_mbus_code,
729ad85094bSMauro Carvalho Chehab 	.enum_frame_size = gc0310_enum_frame_size,
730ad85094bSMauro Carvalho Chehab 	.get_fmt = gc0310_get_fmt,
731ad85094bSMauro Carvalho Chehab 	.set_fmt = gc0310_set_fmt,
732ad85094bSMauro Carvalho Chehab };
733ad85094bSMauro Carvalho Chehab 
734ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_ops gc0310_ops = {
735ad85094bSMauro Carvalho Chehab 	.core = &gc0310_core_ops,
736ad85094bSMauro Carvalho Chehab 	.video = &gc0310_video_ops,
737ad85094bSMauro Carvalho Chehab 	.pad = &gc0310_pad_ops,
738ad85094bSMauro Carvalho Chehab 	.sensor = &gc0310_sensor_ops,
739ad85094bSMauro Carvalho Chehab };
740ad85094bSMauro Carvalho Chehab 
741ed5c2f5fSUwe Kleine-König static void gc0310_remove(struct i2c_client *client)
742ad85094bSMauro Carvalho Chehab {
743ad85094bSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
744ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
745bdfe0bebSMauro Carvalho Chehab 
746ad85094bSMauro Carvalho Chehab 	dev_dbg(&client->dev, "gc0310_remove...\n");
747ad85094bSMauro Carvalho Chehab 
748ad85094bSMauro Carvalho Chehab 	dev->platform_data->csi_cfg(sd, 0);
749ad85094bSMauro Carvalho Chehab 
750ad85094bSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
751ad85094bSMauro Carvalho Chehab 	media_entity_cleanup(&dev->sd.entity);
752ad85094bSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
753ad85094bSMauro Carvalho Chehab 	kfree(dev);
754ad85094bSMauro Carvalho Chehab }
755ad85094bSMauro Carvalho Chehab 
756ad85094bSMauro Carvalho Chehab static int gc0310_probe(struct i2c_client *client)
757ad85094bSMauro Carvalho Chehab {
758ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev;
759ad85094bSMauro Carvalho Chehab 	int ret;
760ad85094bSMauro Carvalho Chehab 	void *pdata;
761ad85094bSMauro Carvalho Chehab 	unsigned int i;
762c03496b3SMauro Carvalho Chehab 
763ad85094bSMauro Carvalho Chehab 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
764ad85094bSMauro Carvalho Chehab 	if (!dev)
765ad85094bSMauro Carvalho Chehab 		return -ENOMEM;
766ad85094bSMauro Carvalho Chehab 
767ad85094bSMauro Carvalho Chehab 	mutex_init(&dev->input_lock);
768ad85094bSMauro Carvalho Chehab 
769e3b14bf8SMauro Carvalho Chehab 	dev->res = &gc0310_res_preview[0];
770bdfe0bebSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops);
771ad85094bSMauro Carvalho Chehab 
772ad85094bSMauro Carvalho Chehab 	pdata = gmin_camera_platform_data(&dev->sd,
773ad85094bSMauro Carvalho Chehab 					  ATOMISP_INPUT_FORMAT_RAW_8,
774ad85094bSMauro Carvalho Chehab 					  atomisp_bayer_order_grbg);
775ad85094bSMauro Carvalho Chehab 	if (!pdata) {
776ad85094bSMauro Carvalho Chehab 		ret = -EINVAL;
777ad85094bSMauro Carvalho Chehab 		goto out_free;
778ad85094bSMauro Carvalho Chehab 	}
779ad85094bSMauro Carvalho Chehab 
780ad85094bSMauro Carvalho Chehab 	ret = gc0310_s_config(&dev->sd, client->irq, pdata);
781ad85094bSMauro Carvalho Chehab 	if (ret)
782ad85094bSMauro Carvalho Chehab 		goto out_free;
783ad85094bSMauro Carvalho Chehab 
784ad85094bSMauro Carvalho Chehab 	ret = atomisp_register_i2c_module(&dev->sd, pdata, RAW_CAMERA);
785ad85094bSMauro Carvalho Chehab 	if (ret)
786ad85094bSMauro Carvalho Chehab 		goto out_free;
787ad85094bSMauro Carvalho Chehab 
788ad85094bSMauro Carvalho Chehab 	dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
789ad85094bSMauro Carvalho Chehab 	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
790ad85094bSMauro Carvalho Chehab 	dev->format.code = MEDIA_BUS_FMT_SGRBG8_1X8;
791ad85094bSMauro Carvalho Chehab 	dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
792ad85094bSMauro Carvalho Chehab 	ret =
793ad85094bSMauro Carvalho Chehab 	    v4l2_ctrl_handler_init(&dev->ctrl_handler,
794ad85094bSMauro Carvalho Chehab 				   ARRAY_SIZE(gc0310_controls));
795ad85094bSMauro Carvalho Chehab 	if (ret) {
796ad85094bSMauro Carvalho Chehab 		gc0310_remove(client);
797ad85094bSMauro Carvalho Chehab 		return ret;
798ad85094bSMauro Carvalho Chehab 	}
799ad85094bSMauro Carvalho Chehab 
800ad85094bSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(gc0310_controls); i++)
801ad85094bSMauro Carvalho Chehab 		v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc0310_controls[i],
802ad85094bSMauro Carvalho Chehab 				     NULL);
803ad85094bSMauro Carvalho Chehab 
804ad85094bSMauro Carvalho Chehab 	if (dev->ctrl_handler.error) {
805ad85094bSMauro Carvalho Chehab 		gc0310_remove(client);
806ad85094bSMauro Carvalho Chehab 		return dev->ctrl_handler.error;
807ad85094bSMauro Carvalho Chehab 	}
808ad85094bSMauro Carvalho Chehab 
809ad85094bSMauro Carvalho Chehab 	/* Use same lock for controls as for everything else. */
810ad85094bSMauro Carvalho Chehab 	dev->ctrl_handler.lock = &dev->input_lock;
811ad85094bSMauro Carvalho Chehab 	dev->sd.ctrl_handler = &dev->ctrl_handler;
812ad85094bSMauro Carvalho Chehab 
813ad85094bSMauro Carvalho Chehab 	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
814ad85094bSMauro Carvalho Chehab 	if (ret)
815ad85094bSMauro Carvalho Chehab 		gc0310_remove(client);
816ad85094bSMauro Carvalho Chehab 
817ad85094bSMauro Carvalho Chehab 	return ret;
818ad85094bSMauro Carvalho Chehab out_free:
819ad85094bSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(&dev->sd);
820ad85094bSMauro Carvalho Chehab 	kfree(dev);
821ad85094bSMauro Carvalho Chehab 	return ret;
822ad85094bSMauro Carvalho Chehab }
823ad85094bSMauro Carvalho Chehab 
824ad85094bSMauro Carvalho Chehab static const struct acpi_device_id gc0310_acpi_match[] = {
825ad85094bSMauro Carvalho Chehab 	{"XXGC0310"},
826ad85094bSMauro Carvalho Chehab 	{"INT0310"},
827ad85094bSMauro Carvalho Chehab 	{},
828ad85094bSMauro Carvalho Chehab };
829ad85094bSMauro Carvalho Chehab MODULE_DEVICE_TABLE(acpi, gc0310_acpi_match);
830ad85094bSMauro Carvalho Chehab 
831ad85094bSMauro Carvalho Chehab static struct i2c_driver gc0310_driver = {
832ad85094bSMauro Carvalho Chehab 	.driver = {
833ad85094bSMauro Carvalho Chehab 		.name = "gc0310",
834ad85094bSMauro Carvalho Chehab 		.acpi_match_table = gc0310_acpi_match,
835ad85094bSMauro Carvalho Chehab 	},
836ad85094bSMauro Carvalho Chehab 	.probe_new = gc0310_probe,
837ad85094bSMauro Carvalho Chehab 	.remove = gc0310_remove,
838ad85094bSMauro Carvalho Chehab };
839ad85094bSMauro Carvalho Chehab module_i2c_driver(gc0310_driver);
840ad85094bSMauro Carvalho Chehab 
841ad85094bSMauro Carvalho Chehab MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>");
842ad85094bSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors");
843ad85094bSMauro Carvalho Chehab MODULE_LICENSE("GPL");
844