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++) {
4965e5ef2fSHans de Goede 		err = i2c_smbus_write_byte_data(client, reglist[i].reg, reglist[i].val);
5065e5ef2fSHans de Goede 		if (err) {
5165e5ef2fSHans de Goede 			dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d",
5265e5ef2fSHans de Goede 				reglist[i].val, reglist[i].reg, err);
53ad85094bSMauro Carvalho Chehab 			return err;
54ad85094bSMauro Carvalho Chehab 		}
5565e5ef2fSHans de Goede 	}
56ad85094bSMauro Carvalho Chehab 
57e1a4b3a7SHans de Goede 	return 0;
58ad85094bSMauro Carvalho Chehab }
59bdfe0bebSMauro Carvalho Chehab 
60ef5fb5d4SHans de Goede static int gc0310_exposure_set(struct gc0310_device *dev, u32 exp)
61ef5fb5d4SHans de Goede {
62ef5fb5d4SHans de Goede 	struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
63ef5fb5d4SHans de Goede 
64ef5fb5d4SHans de Goede 	return i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, exp);
65ef5fb5d4SHans de Goede }
66ef5fb5d4SHans de Goede 
67ef5fb5d4SHans de Goede static int gc0310_gain_set(struct gc0310_device *dev, u32 gain)
68ef5fb5d4SHans de Goede {
69ef5fb5d4SHans de Goede 	struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
70ef5fb5d4SHans de Goede 	u8 again, dgain;
71ef5fb5d4SHans de Goede 	int ret;
72ef5fb5d4SHans de Goede 
73ef5fb5d4SHans de Goede 	/* Taken from original driver, this never sets dgain lower then 32? */
74ef5fb5d4SHans de Goede 
75ef5fb5d4SHans de Goede 	/* Change 0 - 95 to 32 - 127 */
76ef5fb5d4SHans de Goede 	gain += 32;
77ef5fb5d4SHans de Goede 
78ef5fb5d4SHans de Goede 	if (gain < 64) {
79ef5fb5d4SHans de Goede 		again = 0x0; /* sqrt(2) */
80ef5fb5d4SHans de Goede 		dgain = gain;
81ef5fb5d4SHans de Goede 	} else {
82ef5fb5d4SHans de Goede 		again = 0x2; /* 2 * sqrt(2) */
83ef5fb5d4SHans de Goede 		dgain = gain / 2;
84ef5fb5d4SHans de Goede 	}
85ef5fb5d4SHans de Goede 
86ef5fb5d4SHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_AGC_ADJ, again);
87ef5fb5d4SHans de Goede 	if (ret)
88ef5fb5d4SHans de Goede 		return ret;
89ef5fb5d4SHans de Goede 
90ef5fb5d4SHans de Goede 	return i2c_smbus_write_byte_data(client, GC0310_DGC_ADJ, dgain);
91ef5fb5d4SHans de Goede }
92ef5fb5d4SHans de Goede 
93ad85094bSMauro Carvalho Chehab static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl)
94ad85094bSMauro Carvalho Chehab {
95ef5fb5d4SHans de Goede 	struct gc0310_device *dev =
96ef5fb5d4SHans de Goede 		container_of(ctrl->handler, struct gc0310_device, ctrls.handler);
97ef5fb5d4SHans de Goede 	int ret;
98ef5fb5d4SHans de Goede 
99ef5fb5d4SHans de Goede 	if (!dev->power_on)
100ef5fb5d4SHans de Goede 		return 0;
101ad85094bSMauro Carvalho Chehab 
102ad85094bSMauro Carvalho Chehab 	switch (ctrl->id) {
103ef5fb5d4SHans de Goede 	case V4L2_CID_EXPOSURE:
104ef5fb5d4SHans de Goede 		ret = gc0310_exposure_set(dev, ctrl->val);
105ef5fb5d4SHans de Goede 		break;
106ef5fb5d4SHans de Goede 	case V4L2_CID_GAIN:
107ef5fb5d4SHans de Goede 		ret = gc0310_gain_set(dev, ctrl->val);
108ef5fb5d4SHans de Goede 		break;
109ad85094bSMauro Carvalho Chehab 	default:
110ad85094bSMauro Carvalho Chehab 		ret = -EINVAL;
111ef5fb5d4SHans de Goede 		break;
112ad85094bSMauro Carvalho Chehab 	}
113ef5fb5d4SHans de Goede 
114ad85094bSMauro Carvalho Chehab 	return ret;
115ad85094bSMauro Carvalho Chehab }
116ad85094bSMauro Carvalho Chehab 
117ad85094bSMauro Carvalho Chehab static const struct v4l2_ctrl_ops ctrl_ops = {
118ad85094bSMauro Carvalho Chehab 	.s_ctrl = gc0310_s_ctrl,
119ad85094bSMauro Carvalho Chehab };
120ad85094bSMauro Carvalho Chehab 
121ad85094bSMauro Carvalho Chehab static int power_ctrl(struct v4l2_subdev *sd, bool flag)
122ad85094bSMauro Carvalho Chehab {
123ad85094bSMauro Carvalho Chehab 	int ret = 0;
124ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
125bdfe0bebSMauro Carvalho Chehab 
126ad85094bSMauro Carvalho Chehab 	if (!dev || !dev->platform_data)
127ad85094bSMauro Carvalho Chehab 		return -ENODEV;
128ad85094bSMauro Carvalho Chehab 
129ad85094bSMauro Carvalho Chehab 	if (flag) {
130ad85094bSMauro Carvalho Chehab 		/* The upstream module driver (written to Crystal
131ad85094bSMauro Carvalho Chehab 		 * Cove) had this logic to pulse the rails low first.
132ad85094bSMauro Carvalho Chehab 		 * This appears to break things on the MRD7 with the
133ad85094bSMauro Carvalho Chehab 		 * X-Powers PMIC...
134ad85094bSMauro Carvalho Chehab 		 *
135ad85094bSMauro Carvalho Chehab 		 *     ret = dev->platform_data->v1p8_ctrl(sd, 0);
136ad85094bSMauro Carvalho Chehab 		 *     ret |= dev->platform_data->v2p8_ctrl(sd, 0);
137ad85094bSMauro Carvalho Chehab 		 *     mdelay(50);
138ad85094bSMauro Carvalho Chehab 		 */
139ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v1p8_ctrl(sd, 1);
140ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v2p8_ctrl(sd, 1);
141ad85094bSMauro Carvalho Chehab 		usleep_range(10000, 15000);
142ad85094bSMauro Carvalho Chehab 	}
143ad85094bSMauro Carvalho Chehab 
144ad85094bSMauro Carvalho Chehab 	if (!flag || ret) {
145ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v1p8_ctrl(sd, 0);
146ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->v2p8_ctrl(sd, 0);
147ad85094bSMauro Carvalho Chehab 	}
148ad85094bSMauro Carvalho Chehab 	return ret;
149ad85094bSMauro Carvalho Chehab }
150ad85094bSMauro Carvalho Chehab 
151ad85094bSMauro Carvalho Chehab static int gpio_ctrl(struct v4l2_subdev *sd, bool flag)
152ad85094bSMauro Carvalho Chehab {
153ad85094bSMauro Carvalho Chehab 	int ret;
154ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
155ad85094bSMauro Carvalho Chehab 
156ad85094bSMauro Carvalho Chehab 	if (!dev || !dev->platform_data)
157ad85094bSMauro Carvalho Chehab 		return -ENODEV;
158ad85094bSMauro Carvalho Chehab 
159ad85094bSMauro Carvalho Chehab 	/* GPIO0 == "reset" (active low), GPIO1 == "power down" */
160ad85094bSMauro Carvalho Chehab 	if (flag) {
161ad85094bSMauro Carvalho Chehab 		/* Pulse reset, then release power down */
162ad85094bSMauro Carvalho Chehab 		ret = dev->platform_data->gpio0_ctrl(sd, 0);
163ad85094bSMauro Carvalho Chehab 		usleep_range(5000, 10000);
164ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->gpio0_ctrl(sd, 1);
165ad85094bSMauro Carvalho Chehab 		usleep_range(10000, 15000);
166ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->gpio1_ctrl(sd, 0);
167ad85094bSMauro Carvalho Chehab 		usleep_range(10000, 15000);
168ad85094bSMauro Carvalho Chehab 	} else {
169ad85094bSMauro Carvalho Chehab 		ret = dev->platform_data->gpio1_ctrl(sd, 1);
170ad85094bSMauro Carvalho Chehab 		ret |= dev->platform_data->gpio0_ctrl(sd, 0);
171ad85094bSMauro Carvalho Chehab 	}
172ad85094bSMauro Carvalho Chehab 	return ret;
173ad85094bSMauro Carvalho Chehab }
174ad85094bSMauro Carvalho Chehab 
175ad85094bSMauro Carvalho Chehab static int power_up(struct v4l2_subdev *sd)
176ad85094bSMauro Carvalho Chehab {
177ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
178ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
179ad85094bSMauro Carvalho Chehab 	int ret;
180ad85094bSMauro Carvalho Chehab 
181ad85094bSMauro Carvalho Chehab 	if (!dev->platform_data) {
182ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev,
183ad85094bSMauro Carvalho Chehab 			"no camera_sensor_platform_data");
184ad85094bSMauro Carvalho Chehab 		return -ENODEV;
185ad85094bSMauro Carvalho Chehab 	}
186ad85094bSMauro Carvalho Chehab 
187c5fafbadSHans de Goede 	if (dev->power_on)
188c5fafbadSHans de Goede 		return 0; /* Already on */
189c5fafbadSHans de Goede 
190ad85094bSMauro Carvalho Chehab 	/* power control */
191ad85094bSMauro Carvalho Chehab 	ret = power_ctrl(sd, 1);
192ad85094bSMauro Carvalho Chehab 	if (ret)
193ad85094bSMauro Carvalho Chehab 		goto fail_power;
194ad85094bSMauro Carvalho Chehab 
195ad85094bSMauro Carvalho Chehab 	/* flis clock control */
196ad85094bSMauro Carvalho Chehab 	ret = dev->platform_data->flisclk_ctrl(sd, 1);
197ad85094bSMauro Carvalho Chehab 	if (ret)
198ad85094bSMauro Carvalho Chehab 		goto fail_clk;
199ad85094bSMauro Carvalho Chehab 
200ad85094bSMauro Carvalho Chehab 	/* gpio ctrl */
201ad85094bSMauro Carvalho Chehab 	ret = gpio_ctrl(sd, 1);
202ad85094bSMauro Carvalho Chehab 	if (ret) {
203ad85094bSMauro Carvalho Chehab 		ret = gpio_ctrl(sd, 1);
204ad85094bSMauro Carvalho Chehab 		if (ret)
205ad85094bSMauro Carvalho Chehab 			goto fail_gpio;
206ad85094bSMauro Carvalho Chehab 	}
207ad85094bSMauro Carvalho Chehab 
208ad85094bSMauro Carvalho Chehab 	msleep(100);
209ad85094bSMauro Carvalho Chehab 
210c5fafbadSHans de Goede 	dev->power_on = true;
211ad85094bSMauro Carvalho Chehab 	return 0;
212ad85094bSMauro Carvalho Chehab 
213ad85094bSMauro Carvalho Chehab fail_gpio:
214ad85094bSMauro Carvalho Chehab 	dev->platform_data->flisclk_ctrl(sd, 0);
215ad85094bSMauro Carvalho Chehab fail_clk:
216ad85094bSMauro Carvalho Chehab 	power_ctrl(sd, 0);
217ad85094bSMauro Carvalho Chehab fail_power:
218ad85094bSMauro Carvalho Chehab 	dev_err(&client->dev, "sensor power-up failed\n");
219ad85094bSMauro Carvalho Chehab 
220ad85094bSMauro Carvalho Chehab 	return ret;
221ad85094bSMauro Carvalho Chehab }
222ad85094bSMauro Carvalho Chehab 
223ad85094bSMauro Carvalho Chehab static int power_down(struct v4l2_subdev *sd)
224ad85094bSMauro Carvalho Chehab {
225ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
226ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
227ad85094bSMauro Carvalho Chehab 	int ret = 0;
228ad85094bSMauro Carvalho Chehab 
229ad85094bSMauro Carvalho Chehab 	if (!dev->platform_data) {
230ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev,
231ad85094bSMauro Carvalho Chehab 			"no camera_sensor_platform_data");
232ad85094bSMauro Carvalho Chehab 		return -ENODEV;
233ad85094bSMauro Carvalho Chehab 	}
234ad85094bSMauro Carvalho Chehab 
235c5fafbadSHans de Goede 	if (!dev->power_on)
236c5fafbadSHans de Goede 		return 0; /* Already off */
237c5fafbadSHans de Goede 
238ad85094bSMauro Carvalho Chehab 	/* gpio ctrl */
239ad85094bSMauro Carvalho Chehab 	ret = gpio_ctrl(sd, 0);
240ad85094bSMauro Carvalho Chehab 	if (ret) {
241ad85094bSMauro Carvalho Chehab 		ret = gpio_ctrl(sd, 0);
242ad85094bSMauro Carvalho Chehab 		if (ret)
243ad85094bSMauro Carvalho Chehab 			dev_err(&client->dev, "gpio failed 2\n");
244ad85094bSMauro Carvalho Chehab 	}
245ad85094bSMauro Carvalho Chehab 
246ad85094bSMauro Carvalho Chehab 	ret = dev->platform_data->flisclk_ctrl(sd, 0);
247ad85094bSMauro Carvalho Chehab 	if (ret)
248ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "flisclk failed\n");
249ad85094bSMauro Carvalho Chehab 
250ad85094bSMauro Carvalho Chehab 	/* power control */
251ad85094bSMauro Carvalho Chehab 	ret = power_ctrl(sd, 0);
252ad85094bSMauro Carvalho Chehab 	if (ret)
253ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "vprog failed.\n");
254ad85094bSMauro Carvalho Chehab 
255c5fafbadSHans de Goede 	dev->power_on = false;
256ad85094bSMauro Carvalho Chehab 	return ret;
257ad85094bSMauro Carvalho Chehab }
258ad85094bSMauro Carvalho Chehab 
2599783b96aSHans de Goede static struct v4l2_mbus_framefmt *
2609783b96aSHans de Goede gc0310_get_pad_format(struct gc0310_device *dev,
2619783b96aSHans de Goede 		      struct v4l2_subdev_state *state,
2629783b96aSHans de Goede 		      unsigned int pad, enum v4l2_subdev_format_whence which)
263ad85094bSMauro Carvalho Chehab {
2649783b96aSHans de Goede 	if (which == V4L2_SUBDEV_FORMAT_TRY)
2659783b96aSHans de Goede 		return v4l2_subdev_get_try_format(&dev->sd, state, pad);
266ad85094bSMauro Carvalho Chehab 
2679783b96aSHans de Goede 	return &dev->mode.fmt;
268ad85094bSMauro Carvalho Chehab }
269ad85094bSMauro Carvalho Chehab 
2709783b96aSHans de Goede /* The GC0310 currently only supports 1 fixed fmt */
2719783b96aSHans de Goede static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt)
2729783b96aSHans de Goede {
2739783b96aSHans de Goede 	memset(fmt, 0, sizeof(*fmt));
2749783b96aSHans de Goede 	fmt->width = GC0310_NATIVE_WIDTH;
2759783b96aSHans de Goede 	fmt->height = GC0310_NATIVE_HEIGHT;
2769783b96aSHans de Goede 	fmt->field = V4L2_FIELD_NONE;
2779783b96aSHans de Goede 	fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
278ad85094bSMauro Carvalho Chehab }
279ad85094bSMauro Carvalho Chehab 
280ad85094bSMauro Carvalho Chehab static int gc0310_set_fmt(struct v4l2_subdev *sd,
2810d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
282ad85094bSMauro Carvalho Chehab 			  struct v4l2_subdev_format *format)
283ad85094bSMauro Carvalho Chehab {
2849783b96aSHans de Goede 	struct gc0310_device *dev = to_gc0310_sensor(sd);
2859783b96aSHans de Goede 	struct v4l2_mbus_framefmt *fmt;
286bdfe0bebSMauro Carvalho Chehab 
2879783b96aSHans de Goede 	fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which);
2889783b96aSHans de Goede 	gc0310_fill_format(fmt);
289ad85094bSMauro Carvalho Chehab 
2909783b96aSHans de Goede 	format->format = *fmt;
2919783b96aSHans de Goede 	return 0;
292ad85094bSMauro Carvalho Chehab }
293ad85094bSMauro Carvalho Chehab 
294ad85094bSMauro Carvalho Chehab static int gc0310_get_fmt(struct v4l2_subdev *sd,
2950d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
296ad85094bSMauro Carvalho Chehab 			  struct v4l2_subdev_format *format)
297ad85094bSMauro Carvalho Chehab {
298ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
2999783b96aSHans de Goede 	struct v4l2_mbus_framefmt *fmt;
300ad85094bSMauro Carvalho Chehab 
3019783b96aSHans de Goede 	fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which);
3029783b96aSHans de Goede 	format->format = *fmt;
303ad85094bSMauro Carvalho Chehab 	return 0;
304ad85094bSMauro Carvalho Chehab }
305ad85094bSMauro Carvalho Chehab 
306ad85094bSMauro Carvalho Chehab static int gc0310_detect(struct i2c_client *client)
307ad85094bSMauro Carvalho Chehab {
308ad85094bSMauro Carvalho Chehab 	struct i2c_adapter *adapter = client->adapter;
309ad85094bSMauro Carvalho Chehab 	int ret;
310ad85094bSMauro Carvalho Chehab 
311ad85094bSMauro Carvalho Chehab 	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
312ad85094bSMauro Carvalho Chehab 		return -ENODEV;
313ad85094bSMauro Carvalho Chehab 
31465e5ef2fSHans de Goede 	ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H);
31565e5ef2fSHans de Goede 	if (ret < 0) {
31665e5ef2fSHans de Goede 		dev_err(&client->dev, "read sensor_id failed: %d\n", ret);
317ad85094bSMauro Carvalho Chehab 		return -ENODEV;
318ad85094bSMauro Carvalho Chehab 	}
319ad85094bSMauro Carvalho Chehab 
32065e5ef2fSHans de Goede 	dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret);
32165e5ef2fSHans de Goede 
32265e5ef2fSHans de Goede 	if (ret != GC0310_ID) {
32365e5ef2fSHans de Goede 		dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n",
32465e5ef2fSHans de Goede 			ret, GC0310_ID);
325ad85094bSMauro Carvalho Chehab 		return -ENODEV;
326ad85094bSMauro Carvalho Chehab 	}
327ad85094bSMauro Carvalho Chehab 
328ad85094bSMauro Carvalho Chehab 	dev_dbg(&client->dev, "detect gc0310 success\n");
329ad85094bSMauro Carvalho Chehab 
330ad85094bSMauro Carvalho Chehab 	return 0;
331ad85094bSMauro Carvalho Chehab }
332ad85094bSMauro Carvalho Chehab 
333ad85094bSMauro Carvalho Chehab static int gc0310_s_stream(struct v4l2_subdev *sd, int enable)
334ad85094bSMauro Carvalho Chehab {
335ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
336ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
337ad85094bSMauro Carvalho Chehab 	int ret;
338ad85094bSMauro Carvalho Chehab 
3392dfc978aSDeepak R Varma 	dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable);
340ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
341ad85094bSMauro Carvalho Chehab 
342ad85094bSMauro Carvalho Chehab 	if (enable) {
343*b6763b22SHans de Goede 		ret = power_up(sd);
344*b6763b22SHans de Goede 		if (ret)
345*b6763b22SHans de Goede 			goto error_unlock;
346*b6763b22SHans de Goede 
347*b6763b22SHans de Goede 		ret = gc0310_write_reg_array(client, gc0310_reset_register,
348*b6763b22SHans de Goede 					     ARRAY_SIZE(gc0310_reset_register));
349*b6763b22SHans de Goede 		if (ret)
350*b6763b22SHans de Goede 			goto error_power_down;
351*b6763b22SHans de Goede 
352*b6763b22SHans de Goede 		ret = gc0310_write_reg_array(client, gc0310_VGA_30fps,
353*b6763b22SHans de Goede 					     ARRAY_SIZE(gc0310_VGA_30fps));
354*b6763b22SHans de Goede 		if (ret)
355*b6763b22SHans de Goede 			goto error_power_down;
356*b6763b22SHans de Goede 
357*b6763b22SHans de Goede 		/* restore value of all ctrls */
358*b6763b22SHans de Goede 		ret = __v4l2_ctrl_handler_setup(&dev->ctrls.handler);
359*b6763b22SHans de Goede 		if (ret)
360*b6763b22SHans de Goede 			goto error_power_down;
361*b6763b22SHans de Goede 
362ad85094bSMauro Carvalho Chehab 		/* enable per frame MIPI and sensor ctrl reset  */
36365e5ef2fSHans de Goede 		ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30);
3642b2297b1SHans de Goede 		if (ret)
365*b6763b22SHans de Goede 			goto error_power_down;
366ad85094bSMauro Carvalho Chehab 	}
367ad85094bSMauro Carvalho Chehab 
36865e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3);
3692b2297b1SHans de Goede 	if (ret)
370*b6763b22SHans de Goede 		goto error_power_down;
371ad85094bSMauro Carvalho Chehab 
37265e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM,
37365e5ef2fSHans de Goede 					enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING);
3742b2297b1SHans de Goede 	if (ret)
375*b6763b22SHans de Goede 		goto error_power_down;
376ad85094bSMauro Carvalho Chehab 
37765e5ef2fSHans de Goede 	ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0);
3782b2297b1SHans de Goede 	if (ret)
379*b6763b22SHans de Goede 		goto error_power_down;
380*b6763b22SHans de Goede 
381*b6763b22SHans de Goede 	if (!enable)
382*b6763b22SHans de Goede 		power_down(sd);
383ad85094bSMauro Carvalho Chehab 
384ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
3852b2297b1SHans de Goede 	return 0;
3862b2297b1SHans de Goede 
387*b6763b22SHans de Goede error_power_down:
388*b6763b22SHans de Goede 	power_down(sd);
3892b2297b1SHans de Goede error_unlock:
3902b2297b1SHans de Goede 	mutex_unlock(&dev->input_lock);
391ad85094bSMauro Carvalho Chehab 	return ret;
392ad85094bSMauro Carvalho Chehab }
393ad85094bSMauro Carvalho Chehab 
394ad85094bSMauro Carvalho Chehab static int gc0310_s_config(struct v4l2_subdev *sd,
395ad85094bSMauro Carvalho Chehab 			   int irq, void *platform_data)
396ad85094bSMauro Carvalho Chehab {
397ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
398ad85094bSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
399ad85094bSMauro Carvalho Chehab 	int ret = 0;
400ad85094bSMauro Carvalho Chehab 
401ad85094bSMauro Carvalho Chehab 	if (!platform_data)
402ad85094bSMauro Carvalho Chehab 		return -ENODEV;
403ad85094bSMauro Carvalho Chehab 
404ad85094bSMauro Carvalho Chehab 	dev->platform_data =
405ad85094bSMauro Carvalho Chehab 	    (struct camera_sensor_platform_data *)platform_data;
406ad85094bSMauro Carvalho Chehab 
407ad85094bSMauro Carvalho Chehab 	mutex_lock(&dev->input_lock);
408ad85094bSMauro Carvalho Chehab 	/* power off the module, then power on it in future
409ad85094bSMauro Carvalho Chehab 	 * as first power on by board may not fulfill the
410ad85094bSMauro Carvalho Chehab 	 * power on sequqence needed by the module
411ad85094bSMauro Carvalho Chehab 	 */
412c5fafbadSHans de Goede 	dev->power_on = true; /* force power_down() to run */
413ad85094bSMauro Carvalho Chehab 	ret = power_down(sd);
414ad85094bSMauro Carvalho Chehab 	if (ret) {
415ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 power-off err.\n");
416ad85094bSMauro Carvalho Chehab 		goto fail_power_off;
417ad85094bSMauro Carvalho Chehab 	}
418ad85094bSMauro Carvalho Chehab 
419ad85094bSMauro Carvalho Chehab 	ret = power_up(sd);
420ad85094bSMauro Carvalho Chehab 	if (ret) {
421ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 power-up err.\n");
422ad85094bSMauro Carvalho Chehab 		goto fail_power_on;
423ad85094bSMauro Carvalho Chehab 	}
424ad85094bSMauro Carvalho Chehab 
425ad85094bSMauro Carvalho Chehab 	ret = dev->platform_data->csi_cfg(sd, 1);
426ad85094bSMauro Carvalho Chehab 	if (ret)
427ad85094bSMauro Carvalho Chehab 		goto fail_csi_cfg;
428ad85094bSMauro Carvalho Chehab 
429ad85094bSMauro Carvalho Chehab 	/* config & detect sensor */
430ad85094bSMauro Carvalho Chehab 	ret = gc0310_detect(client);
431ad85094bSMauro Carvalho Chehab 	if (ret) {
432ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310_detect err s_config.\n");
433ad85094bSMauro Carvalho Chehab 		goto fail_csi_cfg;
434ad85094bSMauro Carvalho Chehab 	}
435ad85094bSMauro Carvalho Chehab 
436ad85094bSMauro Carvalho Chehab 	/* turn off sensor, after probed */
437ad85094bSMauro Carvalho Chehab 	ret = power_down(sd);
438ad85094bSMauro Carvalho Chehab 	if (ret) {
439ad85094bSMauro Carvalho Chehab 		dev_err(&client->dev, "gc0310 power-off err.\n");
440ad85094bSMauro Carvalho Chehab 		goto fail_csi_cfg;
441ad85094bSMauro Carvalho Chehab 	}
442ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
443ad85094bSMauro Carvalho Chehab 
444ad85094bSMauro Carvalho Chehab 	return 0;
445ad85094bSMauro Carvalho Chehab 
446ad85094bSMauro Carvalho Chehab fail_csi_cfg:
447ad85094bSMauro Carvalho Chehab 	dev->platform_data->csi_cfg(sd, 0);
448ad85094bSMauro Carvalho Chehab fail_power_on:
449ad85094bSMauro Carvalho Chehab 	power_down(sd);
450ad85094bSMauro Carvalho Chehab 	dev_err(&client->dev, "sensor power-gating failed\n");
451ad85094bSMauro Carvalho Chehab fail_power_off:
452ad85094bSMauro Carvalho Chehab 	mutex_unlock(&dev->input_lock);
453ad85094bSMauro Carvalho Chehab 	return ret;
454ad85094bSMauro Carvalho Chehab }
455ad85094bSMauro Carvalho Chehab 
456ad85094bSMauro Carvalho Chehab static int gc0310_g_frame_interval(struct v4l2_subdev *sd,
457ad85094bSMauro Carvalho Chehab 				   struct v4l2_subdev_frame_interval *interval)
458ad85094bSMauro Carvalho Chehab {
459ad85094bSMauro Carvalho Chehab 	interval->interval.numerator = 1;
4609783b96aSHans de Goede 	interval->interval.denominator = GC0310_FPS;
461ad85094bSMauro Carvalho Chehab 
462ad85094bSMauro Carvalho Chehab 	return 0;
463ad85094bSMauro Carvalho Chehab }
464ad85094bSMauro Carvalho Chehab 
465ad85094bSMauro Carvalho Chehab static int gc0310_enum_mbus_code(struct v4l2_subdev *sd,
4660d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
467ad85094bSMauro Carvalho Chehab 				 struct v4l2_subdev_mbus_code_enum *code)
468ad85094bSMauro Carvalho Chehab {
4699783b96aSHans de Goede 	/* We support only a single format */
4709783b96aSHans de Goede 	if (code->index)
471ad85094bSMauro Carvalho Chehab 		return -EINVAL;
472ad85094bSMauro Carvalho Chehab 
473ad85094bSMauro Carvalho Chehab 	code->code = MEDIA_BUS_FMT_SGRBG8_1X8;
474ad85094bSMauro Carvalho Chehab 	return 0;
475ad85094bSMauro Carvalho Chehab }
476ad85094bSMauro Carvalho Chehab 
477ad85094bSMauro Carvalho Chehab static int gc0310_enum_frame_size(struct v4l2_subdev *sd,
4780d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
479ad85094bSMauro Carvalho Chehab 				  struct v4l2_subdev_frame_size_enum *fse)
480ad85094bSMauro Carvalho Chehab {
4819783b96aSHans de Goede 	/* We support only a single resolution */
4829783b96aSHans de Goede 	if (fse->index)
483ad85094bSMauro Carvalho Chehab 		return -EINVAL;
484ad85094bSMauro Carvalho Chehab 
4859783b96aSHans de Goede 	fse->min_width = GC0310_NATIVE_WIDTH;
4869783b96aSHans de Goede 	fse->max_width = GC0310_NATIVE_WIDTH;
4879783b96aSHans de Goede 	fse->min_height = GC0310_NATIVE_HEIGHT;
4889783b96aSHans de Goede 	fse->max_height = GC0310_NATIVE_HEIGHT;
489ad85094bSMauro Carvalho Chehab 
490ad85094bSMauro Carvalho Chehab 	return 0;
491ad85094bSMauro Carvalho Chehab }
492ad85094bSMauro Carvalho Chehab 
493ad85094bSMauro Carvalho Chehab static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
494ad85094bSMauro Carvalho Chehab {
4959783b96aSHans de Goede 	*frames = GC0310_SKIP_FRAMES;
496ad85094bSMauro Carvalho Chehab 	return 0;
497ad85094bSMauro Carvalho Chehab }
498ad85094bSMauro Carvalho Chehab 
499ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = {
500ad85094bSMauro Carvalho Chehab 	.g_skip_frames	= gc0310_g_skip_frames,
501ad85094bSMauro Carvalho Chehab };
502ad85094bSMauro Carvalho Chehab 
503ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops gc0310_video_ops = {
504ad85094bSMauro Carvalho Chehab 	.s_stream = gc0310_s_stream,
505ad85094bSMauro Carvalho Chehab 	.g_frame_interval = gc0310_g_frame_interval,
506ad85094bSMauro Carvalho Chehab };
507ad85094bSMauro Carvalho Chehab 
508ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_pad_ops gc0310_pad_ops = {
509ad85094bSMauro Carvalho Chehab 	.enum_mbus_code = gc0310_enum_mbus_code,
510ad85094bSMauro Carvalho Chehab 	.enum_frame_size = gc0310_enum_frame_size,
511ad85094bSMauro Carvalho Chehab 	.get_fmt = gc0310_get_fmt,
512ad85094bSMauro Carvalho Chehab 	.set_fmt = gc0310_set_fmt,
513ad85094bSMauro Carvalho Chehab };
514ad85094bSMauro Carvalho Chehab 
515ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_ops gc0310_ops = {
516ad85094bSMauro Carvalho Chehab 	.video = &gc0310_video_ops,
517ad85094bSMauro Carvalho Chehab 	.pad = &gc0310_pad_ops,
518ad85094bSMauro Carvalho Chehab 	.sensor = &gc0310_sensor_ops,
519ad85094bSMauro Carvalho Chehab };
520ad85094bSMauro Carvalho Chehab 
521ef5fb5d4SHans de Goede static int gc0310_init_controls(struct gc0310_device *dev)
522ef5fb5d4SHans de Goede {
523ef5fb5d4SHans de Goede 	struct v4l2_ctrl_handler *hdl = &dev->ctrls.handler;
524ef5fb5d4SHans de Goede 
525ef5fb5d4SHans de Goede 	v4l2_ctrl_handler_init(hdl, 2);
526ef5fb5d4SHans de Goede 
527ef5fb5d4SHans de Goede 	/* Use the same lock for controls as for everything else */
528ef5fb5d4SHans de Goede 	hdl->lock = &dev->input_lock;
529ef5fb5d4SHans de Goede 	dev->sd.ctrl_handler = hdl;
530ef5fb5d4SHans de Goede 
531ef5fb5d4SHans de Goede 	dev->ctrls.exposure =
532ef5fb5d4SHans de Goede 		v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023);
533ef5fb5d4SHans de Goede 
534ef5fb5d4SHans de Goede 	/* 32 steps at base gain 1 + 64 half steps at base gain 2 */
535ef5fb5d4SHans de Goede 	dev->ctrls.gain =
536ef5fb5d4SHans de Goede 		v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31);
537ef5fb5d4SHans de Goede 
538ef5fb5d4SHans de Goede 	return hdl->error;
539ef5fb5d4SHans de Goede }
540ef5fb5d4SHans de Goede 
541ed5c2f5fSUwe Kleine-König static void gc0310_remove(struct i2c_client *client)
542ad85094bSMauro Carvalho Chehab {
543ad85094bSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
544ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev = to_gc0310_sensor(sd);
545bdfe0bebSMauro Carvalho Chehab 
546ad85094bSMauro Carvalho Chehab 	dev_dbg(&client->dev, "gc0310_remove...\n");
547ad85094bSMauro Carvalho Chehab 
548ad85094bSMauro Carvalho Chehab 	dev->platform_data->csi_cfg(sd, 0);
549ad85094bSMauro Carvalho Chehab 
550ad85094bSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
551ad85094bSMauro Carvalho Chehab 	media_entity_cleanup(&dev->sd.entity);
552ef5fb5d4SHans de Goede 	v4l2_ctrl_handler_free(&dev->ctrls.handler);
553ad85094bSMauro Carvalho Chehab 	kfree(dev);
554ad85094bSMauro Carvalho Chehab }
555ad85094bSMauro Carvalho Chehab 
556ad85094bSMauro Carvalho Chehab static int gc0310_probe(struct i2c_client *client)
557ad85094bSMauro Carvalho Chehab {
558ad85094bSMauro Carvalho Chehab 	struct gc0310_device *dev;
559ad85094bSMauro Carvalho Chehab 	int ret;
560ad85094bSMauro Carvalho Chehab 	void *pdata;
561c03496b3SMauro Carvalho Chehab 
562ad85094bSMauro Carvalho Chehab 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
563ad85094bSMauro Carvalho Chehab 	if (!dev)
564ad85094bSMauro Carvalho Chehab 		return -ENOMEM;
565ad85094bSMauro Carvalho Chehab 
566ad85094bSMauro Carvalho Chehab 	mutex_init(&dev->input_lock);
567bdfe0bebSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops);
5689783b96aSHans de Goede 	gc0310_fill_format(&dev->mode.fmt);
569ad85094bSMauro Carvalho Chehab 
570ad85094bSMauro Carvalho Chehab 	pdata = gmin_camera_platform_data(&dev->sd,
571ad85094bSMauro Carvalho Chehab 					  ATOMISP_INPUT_FORMAT_RAW_8,
572ad85094bSMauro Carvalho Chehab 					  atomisp_bayer_order_grbg);
573ad85094bSMauro Carvalho Chehab 	if (!pdata) {
574ad85094bSMauro Carvalho Chehab 		ret = -EINVAL;
575ad85094bSMauro Carvalho Chehab 		goto out_free;
576ad85094bSMauro Carvalho Chehab 	}
577ad85094bSMauro Carvalho Chehab 
578ad85094bSMauro Carvalho Chehab 	ret = gc0310_s_config(&dev->sd, client->irq, pdata);
579ad85094bSMauro Carvalho Chehab 	if (ret)
580ad85094bSMauro Carvalho Chehab 		goto out_free;
581ad85094bSMauro Carvalho Chehab 
582ad85094bSMauro Carvalho Chehab 	ret = atomisp_register_i2c_module(&dev->sd, pdata, RAW_CAMERA);
583ad85094bSMauro Carvalho Chehab 	if (ret)
584ad85094bSMauro Carvalho Chehab 		goto out_free;
585ad85094bSMauro Carvalho Chehab 
586ad85094bSMauro Carvalho Chehab 	dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
587ad85094bSMauro Carvalho Chehab 	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
588ad85094bSMauro Carvalho Chehab 	dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
589ef5fb5d4SHans de Goede 
590ef5fb5d4SHans de Goede 	ret = gc0310_init_controls(dev);
591ad85094bSMauro Carvalho Chehab 	if (ret) {
592ad85094bSMauro Carvalho Chehab 		gc0310_remove(client);
593ad85094bSMauro Carvalho Chehab 		return ret;
594ad85094bSMauro Carvalho Chehab 	}
595ad85094bSMauro Carvalho Chehab 
596ad85094bSMauro Carvalho Chehab 	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
597ad85094bSMauro Carvalho Chehab 	if (ret)
598ad85094bSMauro Carvalho Chehab 		gc0310_remove(client);
599ad85094bSMauro Carvalho Chehab 
600ad85094bSMauro Carvalho Chehab 	return ret;
601ad85094bSMauro Carvalho Chehab out_free:
602ad85094bSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(&dev->sd);
603ad85094bSMauro Carvalho Chehab 	kfree(dev);
604ad85094bSMauro Carvalho Chehab 	return ret;
605ad85094bSMauro Carvalho Chehab }
606ad85094bSMauro Carvalho Chehab 
607ad85094bSMauro Carvalho Chehab static const struct acpi_device_id gc0310_acpi_match[] = {
608ad85094bSMauro Carvalho Chehab 	{"XXGC0310"},
609ad85094bSMauro Carvalho Chehab 	{"INT0310"},
610ad85094bSMauro Carvalho Chehab 	{},
611ad85094bSMauro Carvalho Chehab };
612ad85094bSMauro Carvalho Chehab MODULE_DEVICE_TABLE(acpi, gc0310_acpi_match);
613ad85094bSMauro Carvalho Chehab 
614ad85094bSMauro Carvalho Chehab static struct i2c_driver gc0310_driver = {
615ad85094bSMauro Carvalho Chehab 	.driver = {
616ad85094bSMauro Carvalho Chehab 		.name = "gc0310",
617ad85094bSMauro Carvalho Chehab 		.acpi_match_table = gc0310_acpi_match,
618ad85094bSMauro Carvalho Chehab 	},
619ad85094bSMauro Carvalho Chehab 	.probe_new = gc0310_probe,
620ad85094bSMauro Carvalho Chehab 	.remove = gc0310_remove,
621ad85094bSMauro Carvalho Chehab };
622ad85094bSMauro Carvalho Chehab module_i2c_driver(gc0310_driver);
623ad85094bSMauro Carvalho Chehab 
624ad85094bSMauro Carvalho Chehab MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>");
625ad85094bSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors");
626ad85094bSMauro Carvalho Chehab MODULE_LICENSE("GPL");
627