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. 62ec5bfe0SHans de Goede * Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com> 7ad85094bSMauro Carvalho Chehab * 8ad85094bSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or 9ad85094bSMauro Carvalho Chehab * modify it under the terms of the GNU General Public License version 10ad85094bSMauro Carvalho Chehab * 2 as published by the Free Software Foundation. 11ad85094bSMauro Carvalho Chehab * 12ad85094bSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 13ad85094bSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 14ad85094bSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15ad85094bSMauro Carvalho Chehab * GNU General Public License for more details. 16ad85094bSMauro Carvalho Chehab * 17ad85094bSMauro Carvalho Chehab */ 18ad85094bSMauro Carvalho Chehab 19ad85094bSMauro Carvalho Chehab #include <linux/delay.h> 2063558464SHans de Goede #include <linux/errno.h> 212ec5bfe0SHans de Goede #include <linux/gpio/consumer.h> 22ad85094bSMauro Carvalho Chehab #include <linux/i2c.h> 2363558464SHans de Goede #include <linux/kernel.h> 2463558464SHans de Goede #include <linux/module.h> 252726c899SHans de Goede #include <linux/pm_runtime.h> 2663558464SHans de Goede #include <linux/string.h> 2763558464SHans de Goede #include <linux/types.h> 28ad85094bSMauro Carvalho Chehab 2963558464SHans de Goede #include <media/v4l2-device.h> 3063558464SHans de Goede 3163558464SHans de Goede #include "../include/linux/atomisp_gmin_platform.h" 32ad85094bSMauro Carvalho Chehab #include "gc0310.h" 33ad85094bSMauro Carvalho Chehab 34ad85094bSMauro Carvalho Chehab /* 35ad85094bSMauro Carvalho Chehab * gc0310_write_reg_array - Initializes a list of GC0310 registers 36ad85094bSMauro Carvalho Chehab * @client: i2c driver client structure 37ad85094bSMauro Carvalho Chehab * @reglist: list of registers to be written 38bfe06aeeSHans de Goede * @count: number of register, value pairs in the list 39ad85094bSMauro Carvalho Chehab */ 40ad85094bSMauro Carvalho Chehab static int gc0310_write_reg_array(struct i2c_client *client, 41bfe06aeeSHans de Goede const struct gc0310_reg *reglist, int count) 42ad85094bSMauro Carvalho Chehab { 43bfe06aeeSHans de Goede int i, err; 44ad85094bSMauro Carvalho Chehab 45bfe06aeeSHans de Goede for (i = 0; i < count; i++) { 4665e5ef2fSHans de Goede err = i2c_smbus_write_byte_data(client, reglist[i].reg, reglist[i].val); 4765e5ef2fSHans de Goede if (err) { 4865e5ef2fSHans de Goede dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d", 4965e5ef2fSHans de Goede reglist[i].val, reglist[i].reg, err); 50ad85094bSMauro Carvalho Chehab return err; 51ad85094bSMauro Carvalho Chehab } 5265e5ef2fSHans de Goede } 53ad85094bSMauro Carvalho Chehab 54e1a4b3a7SHans de Goede return 0; 55ad85094bSMauro Carvalho Chehab } 56bdfe0bebSMauro Carvalho Chehab 57ef5fb5d4SHans de Goede static int gc0310_exposure_set(struct gc0310_device *dev, u32 exp) 58ef5fb5d4SHans de Goede { 59ef5fb5d4SHans de Goede struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); 60ef5fb5d4SHans de Goede 61ef5fb5d4SHans de Goede return i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, exp); 62ef5fb5d4SHans de Goede } 63ef5fb5d4SHans de Goede 64ef5fb5d4SHans de Goede static int gc0310_gain_set(struct gc0310_device *dev, u32 gain) 65ef5fb5d4SHans de Goede { 66ef5fb5d4SHans de Goede struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); 67ef5fb5d4SHans de Goede u8 again, dgain; 68ef5fb5d4SHans de Goede int ret; 69ef5fb5d4SHans de Goede 70ef5fb5d4SHans de Goede /* Taken from original driver, this never sets dgain lower then 32? */ 71ef5fb5d4SHans de Goede 72ef5fb5d4SHans de Goede /* Change 0 - 95 to 32 - 127 */ 73ef5fb5d4SHans de Goede gain += 32; 74ef5fb5d4SHans de Goede 75ef5fb5d4SHans de Goede if (gain < 64) { 76ef5fb5d4SHans de Goede again = 0x0; /* sqrt(2) */ 77ef5fb5d4SHans de Goede dgain = gain; 78ef5fb5d4SHans de Goede } else { 79ef5fb5d4SHans de Goede again = 0x2; /* 2 * sqrt(2) */ 80ef5fb5d4SHans de Goede dgain = gain / 2; 81ef5fb5d4SHans de Goede } 82ef5fb5d4SHans de Goede 83ef5fb5d4SHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_AGC_ADJ, again); 84ef5fb5d4SHans de Goede if (ret) 85ef5fb5d4SHans de Goede return ret; 86ef5fb5d4SHans de Goede 87ef5fb5d4SHans de Goede return i2c_smbus_write_byte_data(client, GC0310_DGC_ADJ, dgain); 88ef5fb5d4SHans de Goede } 89ef5fb5d4SHans de Goede 90ad85094bSMauro Carvalho Chehab static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) 91ad85094bSMauro Carvalho Chehab { 92ef5fb5d4SHans de Goede struct gc0310_device *dev = 93ef5fb5d4SHans de Goede container_of(ctrl->handler, struct gc0310_device, ctrls.handler); 94ef5fb5d4SHans de Goede int ret; 95ef5fb5d4SHans de Goede 962726c899SHans de Goede /* Only apply changes to the controls if the device is powered up */ 972726c899SHans de Goede if (!pm_runtime_get_if_in_use(dev->sd.dev)) 98ef5fb5d4SHans de Goede return 0; 99ad85094bSMauro Carvalho Chehab 100ad85094bSMauro Carvalho Chehab switch (ctrl->id) { 101ef5fb5d4SHans de Goede case V4L2_CID_EXPOSURE: 102ef5fb5d4SHans de Goede ret = gc0310_exposure_set(dev, ctrl->val); 103ef5fb5d4SHans de Goede break; 104ef5fb5d4SHans de Goede case V4L2_CID_GAIN: 105ef5fb5d4SHans de Goede ret = gc0310_gain_set(dev, ctrl->val); 106ef5fb5d4SHans de Goede break; 107ad85094bSMauro Carvalho Chehab default: 108ad85094bSMauro Carvalho Chehab ret = -EINVAL; 109ef5fb5d4SHans de Goede break; 110ad85094bSMauro Carvalho Chehab } 111ef5fb5d4SHans de Goede 1122726c899SHans de Goede pm_runtime_put(dev->sd.dev); 113ad85094bSMauro Carvalho Chehab return ret; 114ad85094bSMauro Carvalho Chehab } 115ad85094bSMauro Carvalho Chehab 116ad85094bSMauro Carvalho Chehab static const struct v4l2_ctrl_ops ctrl_ops = { 117ad85094bSMauro Carvalho Chehab .s_ctrl = gc0310_s_ctrl, 118ad85094bSMauro Carvalho Chehab }; 119ad85094bSMauro Carvalho Chehab 1209783b96aSHans de Goede static struct v4l2_mbus_framefmt * 1219783b96aSHans de Goede gc0310_get_pad_format(struct gc0310_device *dev, 1229783b96aSHans de Goede struct v4l2_subdev_state *state, 1239783b96aSHans de Goede unsigned int pad, enum v4l2_subdev_format_whence which) 124ad85094bSMauro Carvalho Chehab { 1259783b96aSHans de Goede if (which == V4L2_SUBDEV_FORMAT_TRY) 1269783b96aSHans de Goede return v4l2_subdev_get_try_format(&dev->sd, state, pad); 127ad85094bSMauro Carvalho Chehab 1289783b96aSHans de Goede return &dev->mode.fmt; 129ad85094bSMauro Carvalho Chehab } 130ad85094bSMauro Carvalho Chehab 1319783b96aSHans de Goede /* The GC0310 currently only supports 1 fixed fmt */ 1329783b96aSHans de Goede static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt) 1339783b96aSHans de Goede { 1349783b96aSHans de Goede memset(fmt, 0, sizeof(*fmt)); 1359783b96aSHans de Goede fmt->width = GC0310_NATIVE_WIDTH; 1369783b96aSHans de Goede fmt->height = GC0310_NATIVE_HEIGHT; 1379783b96aSHans de Goede fmt->field = V4L2_FIELD_NONE; 1389783b96aSHans de Goede fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; 139ad85094bSMauro Carvalho Chehab } 140ad85094bSMauro Carvalho Chehab 141ad85094bSMauro Carvalho Chehab static int gc0310_set_fmt(struct v4l2_subdev *sd, 1420d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 143ad85094bSMauro Carvalho Chehab struct v4l2_subdev_format *format) 144ad85094bSMauro Carvalho Chehab { 1459783b96aSHans de Goede struct gc0310_device *dev = to_gc0310_sensor(sd); 1469783b96aSHans de Goede struct v4l2_mbus_framefmt *fmt; 147bdfe0bebSMauro Carvalho Chehab 1489783b96aSHans de Goede fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which); 1499783b96aSHans de Goede gc0310_fill_format(fmt); 150ad85094bSMauro Carvalho Chehab 1519783b96aSHans de Goede format->format = *fmt; 1529783b96aSHans de Goede return 0; 153ad85094bSMauro Carvalho Chehab } 154ad85094bSMauro Carvalho Chehab 155ad85094bSMauro Carvalho Chehab static int gc0310_get_fmt(struct v4l2_subdev *sd, 1560d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 157ad85094bSMauro Carvalho Chehab struct v4l2_subdev_format *format) 158ad85094bSMauro Carvalho Chehab { 159ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 1609783b96aSHans de Goede struct v4l2_mbus_framefmt *fmt; 161ad85094bSMauro Carvalho Chehab 1629783b96aSHans de Goede fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which); 1639783b96aSHans de Goede format->format = *fmt; 164ad85094bSMauro Carvalho Chehab return 0; 165ad85094bSMauro Carvalho Chehab } 166ad85094bSMauro Carvalho Chehab 167ad85094bSMauro Carvalho Chehab static int gc0310_detect(struct i2c_client *client) 168ad85094bSMauro Carvalho Chehab { 169ad85094bSMauro Carvalho Chehab struct i2c_adapter *adapter = client->adapter; 170ad85094bSMauro Carvalho Chehab int ret; 171ad85094bSMauro Carvalho Chehab 172ad85094bSMauro Carvalho Chehab if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) 173ad85094bSMauro Carvalho Chehab return -ENODEV; 174ad85094bSMauro Carvalho Chehab 175*f5e381ffSHans de Goede ret = pm_runtime_get_sync(&client->dev); 176*f5e381ffSHans de Goede if (ret >= 0) 17765e5ef2fSHans de Goede ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H); 178*f5e381ffSHans de Goede pm_runtime_put(&client->dev); 17965e5ef2fSHans de Goede if (ret < 0) { 18065e5ef2fSHans de Goede dev_err(&client->dev, "read sensor_id failed: %d\n", ret); 181ad85094bSMauro Carvalho Chehab return -ENODEV; 182ad85094bSMauro Carvalho Chehab } 183ad85094bSMauro Carvalho Chehab 18465e5ef2fSHans de Goede dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret); 18565e5ef2fSHans de Goede 18665e5ef2fSHans de Goede if (ret != GC0310_ID) { 18765e5ef2fSHans de Goede dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n", 18865e5ef2fSHans de Goede ret, GC0310_ID); 189ad85094bSMauro Carvalho Chehab return -ENODEV; 190ad85094bSMauro Carvalho Chehab } 191ad85094bSMauro Carvalho Chehab 192ad85094bSMauro Carvalho Chehab dev_dbg(&client->dev, "detect gc0310 success\n"); 193ad85094bSMauro Carvalho Chehab 194ad85094bSMauro Carvalho Chehab return 0; 195ad85094bSMauro Carvalho Chehab } 196ad85094bSMauro Carvalho Chehab 197ad85094bSMauro Carvalho Chehab static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) 198ad85094bSMauro Carvalho Chehab { 199ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 200ad85094bSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 2012726c899SHans de Goede int ret = 0; 202ad85094bSMauro Carvalho Chehab 2032dfc978aSDeepak R Varma dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable); 204ad85094bSMauro Carvalho Chehab mutex_lock(&dev->input_lock); 205ad85094bSMauro Carvalho Chehab 2062726c899SHans de Goede if (dev->is_streaming == enable) { 2072726c899SHans de Goede dev_warn(&client->dev, "stream already %s\n", enable ? "started" : "stopped"); 208b6763b22SHans de Goede goto error_unlock; 2092726c899SHans de Goede } 2102726c899SHans de Goede 2112726c899SHans de Goede if (enable) { 2122726c899SHans de Goede ret = pm_runtime_get_sync(&client->dev); 2132726c899SHans de Goede if (ret < 0) 2142726c899SHans de Goede goto error_power_down; 215b6763b22SHans de Goede 2162ec5bfe0SHans de Goede msleep(100); 2172ec5bfe0SHans de Goede 218b6763b22SHans de Goede ret = gc0310_write_reg_array(client, gc0310_reset_register, 219b6763b22SHans de Goede ARRAY_SIZE(gc0310_reset_register)); 220b6763b22SHans de Goede if (ret) 221b6763b22SHans de Goede goto error_power_down; 222b6763b22SHans de Goede 223b6763b22SHans de Goede ret = gc0310_write_reg_array(client, gc0310_VGA_30fps, 224b6763b22SHans de Goede ARRAY_SIZE(gc0310_VGA_30fps)); 225b6763b22SHans de Goede if (ret) 226b6763b22SHans de Goede goto error_power_down; 227b6763b22SHans de Goede 228b6763b22SHans de Goede /* restore value of all ctrls */ 229b6763b22SHans de Goede ret = __v4l2_ctrl_handler_setup(&dev->ctrls.handler); 230b6763b22SHans de Goede if (ret) 231b6763b22SHans de Goede goto error_power_down; 232b6763b22SHans de Goede 233ad85094bSMauro Carvalho Chehab /* enable per frame MIPI and sensor ctrl reset */ 23465e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30); 2352b2297b1SHans de Goede if (ret) 236b6763b22SHans de Goede goto error_power_down; 237ad85094bSMauro Carvalho Chehab } 238ad85094bSMauro Carvalho Chehab 23965e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3); 2402b2297b1SHans de Goede if (ret) 241b6763b22SHans de Goede goto error_power_down; 242ad85094bSMauro Carvalho Chehab 24365e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM, 24465e5ef2fSHans de Goede enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING); 2452b2297b1SHans de Goede if (ret) 246b6763b22SHans de Goede goto error_power_down; 247ad85094bSMauro Carvalho Chehab 24865e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0); 2492b2297b1SHans de Goede if (ret) 250b6763b22SHans de Goede goto error_power_down; 251b6763b22SHans de Goede 252b6763b22SHans de Goede if (!enable) 2532726c899SHans de Goede pm_runtime_put(&client->dev); 254ad85094bSMauro Carvalho Chehab 2552726c899SHans de Goede dev->is_streaming = enable; 256ad85094bSMauro Carvalho Chehab mutex_unlock(&dev->input_lock); 2572b2297b1SHans de Goede return 0; 2582b2297b1SHans de Goede 259b6763b22SHans de Goede error_power_down: 2602726c899SHans de Goede pm_runtime_put(&client->dev); 2612726c899SHans de Goede dev->is_streaming = false; 2622b2297b1SHans de Goede error_unlock: 2632b2297b1SHans de Goede mutex_unlock(&dev->input_lock); 264ad85094bSMauro Carvalho Chehab return ret; 265ad85094bSMauro Carvalho Chehab } 266ad85094bSMauro Carvalho Chehab 267ad85094bSMauro Carvalho Chehab static int gc0310_g_frame_interval(struct v4l2_subdev *sd, 268ad85094bSMauro Carvalho Chehab struct v4l2_subdev_frame_interval *interval) 269ad85094bSMauro Carvalho Chehab { 270ad85094bSMauro Carvalho Chehab interval->interval.numerator = 1; 2719783b96aSHans de Goede interval->interval.denominator = GC0310_FPS; 272ad85094bSMauro Carvalho Chehab 273ad85094bSMauro Carvalho Chehab return 0; 274ad85094bSMauro Carvalho Chehab } 275ad85094bSMauro Carvalho Chehab 276ad85094bSMauro Carvalho Chehab static int gc0310_enum_mbus_code(struct v4l2_subdev *sd, 2770d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 278ad85094bSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 279ad85094bSMauro Carvalho Chehab { 2809783b96aSHans de Goede /* We support only a single format */ 2819783b96aSHans de Goede if (code->index) 282ad85094bSMauro Carvalho Chehab return -EINVAL; 283ad85094bSMauro Carvalho Chehab 284ad85094bSMauro Carvalho Chehab code->code = MEDIA_BUS_FMT_SGRBG8_1X8; 285ad85094bSMauro Carvalho Chehab return 0; 286ad85094bSMauro Carvalho Chehab } 287ad85094bSMauro Carvalho Chehab 288ad85094bSMauro Carvalho Chehab static int gc0310_enum_frame_size(struct v4l2_subdev *sd, 2890d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 290ad85094bSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 291ad85094bSMauro Carvalho Chehab { 2929783b96aSHans de Goede /* We support only a single resolution */ 2939783b96aSHans de Goede if (fse->index) 294ad85094bSMauro Carvalho Chehab return -EINVAL; 295ad85094bSMauro Carvalho Chehab 2969783b96aSHans de Goede fse->min_width = GC0310_NATIVE_WIDTH; 2979783b96aSHans de Goede fse->max_width = GC0310_NATIVE_WIDTH; 2989783b96aSHans de Goede fse->min_height = GC0310_NATIVE_HEIGHT; 2999783b96aSHans de Goede fse->max_height = GC0310_NATIVE_HEIGHT; 300ad85094bSMauro Carvalho Chehab 301ad85094bSMauro Carvalho Chehab return 0; 302ad85094bSMauro Carvalho Chehab } 303ad85094bSMauro Carvalho Chehab 304ad85094bSMauro Carvalho Chehab static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) 305ad85094bSMauro Carvalho Chehab { 3069783b96aSHans de Goede *frames = GC0310_SKIP_FRAMES; 307ad85094bSMauro Carvalho Chehab return 0; 308ad85094bSMauro Carvalho Chehab } 309ad85094bSMauro Carvalho Chehab 310ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = { 311ad85094bSMauro Carvalho Chehab .g_skip_frames = gc0310_g_skip_frames, 312ad85094bSMauro Carvalho Chehab }; 313ad85094bSMauro Carvalho Chehab 314ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops gc0310_video_ops = { 315ad85094bSMauro Carvalho Chehab .s_stream = gc0310_s_stream, 316ad85094bSMauro Carvalho Chehab .g_frame_interval = gc0310_g_frame_interval, 317ad85094bSMauro Carvalho Chehab }; 318ad85094bSMauro Carvalho Chehab 319ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { 320ad85094bSMauro Carvalho Chehab .enum_mbus_code = gc0310_enum_mbus_code, 321ad85094bSMauro Carvalho Chehab .enum_frame_size = gc0310_enum_frame_size, 322ad85094bSMauro Carvalho Chehab .get_fmt = gc0310_get_fmt, 323ad85094bSMauro Carvalho Chehab .set_fmt = gc0310_set_fmt, 324ad85094bSMauro Carvalho Chehab }; 325ad85094bSMauro Carvalho Chehab 326ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_ops gc0310_ops = { 327ad85094bSMauro Carvalho Chehab .video = &gc0310_video_ops, 328ad85094bSMauro Carvalho Chehab .pad = &gc0310_pad_ops, 329ad85094bSMauro Carvalho Chehab .sensor = &gc0310_sensor_ops, 330ad85094bSMauro Carvalho Chehab }; 331ad85094bSMauro Carvalho Chehab 332ef5fb5d4SHans de Goede static int gc0310_init_controls(struct gc0310_device *dev) 333ef5fb5d4SHans de Goede { 334ef5fb5d4SHans de Goede struct v4l2_ctrl_handler *hdl = &dev->ctrls.handler; 335ef5fb5d4SHans de Goede 336ef5fb5d4SHans de Goede v4l2_ctrl_handler_init(hdl, 2); 337ef5fb5d4SHans de Goede 338ef5fb5d4SHans de Goede /* Use the same lock for controls as for everything else */ 339ef5fb5d4SHans de Goede hdl->lock = &dev->input_lock; 340ef5fb5d4SHans de Goede dev->sd.ctrl_handler = hdl; 341ef5fb5d4SHans de Goede 342ef5fb5d4SHans de Goede dev->ctrls.exposure = 343ef5fb5d4SHans de Goede v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023); 344ef5fb5d4SHans de Goede 345ef5fb5d4SHans de Goede /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ 346ef5fb5d4SHans de Goede dev->ctrls.gain = 347ef5fb5d4SHans de Goede v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31); 348ef5fb5d4SHans de Goede 349ef5fb5d4SHans de Goede return hdl->error; 350ef5fb5d4SHans de Goede } 351ef5fb5d4SHans de Goede 352ed5c2f5fSUwe Kleine-König static void gc0310_remove(struct i2c_client *client) 353ad85094bSMauro Carvalho Chehab { 354ad85094bSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 355ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 356bdfe0bebSMauro Carvalho Chehab 357ad85094bSMauro Carvalho Chehab dev_dbg(&client->dev, "gc0310_remove...\n"); 358ad85094bSMauro Carvalho Chehab 3592ec5bfe0SHans de Goede atomisp_unregister_subdev(sd); 360ad85094bSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 361ad85094bSMauro Carvalho Chehab media_entity_cleanup(&dev->sd.entity); 362ef5fb5d4SHans de Goede v4l2_ctrl_handler_free(&dev->ctrls.handler); 3632746a966SHans de Goede mutex_destroy(&dev->input_lock); 3642726c899SHans de Goede pm_runtime_disable(&client->dev); 365ad85094bSMauro Carvalho Chehab } 366ad85094bSMauro Carvalho Chehab 367ad85094bSMauro Carvalho Chehab static int gc0310_probe(struct i2c_client *client) 368ad85094bSMauro Carvalho Chehab { 369ad85094bSMauro Carvalho Chehab struct gc0310_device *dev; 370ad85094bSMauro Carvalho Chehab int ret; 371c03496b3SMauro Carvalho Chehab 372340b4dd6SHans de Goede dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); 373ad85094bSMauro Carvalho Chehab if (!dev) 374ad85094bSMauro Carvalho Chehab return -ENOMEM; 375ad85094bSMauro Carvalho Chehab 3762ec5bfe0SHans de Goede ret = v4l2_get_acpi_sensor_info(&client->dev, NULL); 3772ec5bfe0SHans de Goede if (ret) 3782ec5bfe0SHans de Goede return ret; 3792ec5bfe0SHans de Goede 3802ec5bfe0SHans de Goede dev->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); 3812ec5bfe0SHans de Goede if (IS_ERR(dev->reset)) 3822ec5bfe0SHans de Goede return dev_err_probe(&client->dev, PTR_ERR(dev->reset), 3832ec5bfe0SHans de Goede "getting reset GPIO\n"); 3842ec5bfe0SHans de Goede 3852ec5bfe0SHans de Goede dev->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); 3862ec5bfe0SHans de Goede if (IS_ERR(dev->powerdown)) 3872ec5bfe0SHans de Goede return dev_err_probe(&client->dev, PTR_ERR(dev->powerdown), 3882ec5bfe0SHans de Goede "getting powerdown GPIO\n"); 3892ec5bfe0SHans de Goede 390ad85094bSMauro Carvalho Chehab mutex_init(&dev->input_lock); 391bdfe0bebSMauro Carvalho Chehab v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops); 3929783b96aSHans de Goede gc0310_fill_format(&dev->mode.fmt); 393ad85094bSMauro Carvalho Chehab 3942726c899SHans de Goede pm_runtime_set_suspended(&client->dev); 3952726c899SHans de Goede pm_runtime_enable(&client->dev); 3962726c899SHans de Goede pm_runtime_set_autosuspend_delay(&client->dev, 1000); 3972726c899SHans de Goede pm_runtime_use_autosuspend(&client->dev); 3982726c899SHans de Goede 399*f5e381ffSHans de Goede ret = gc0310_detect(client); 4002726c899SHans de Goede if (ret) { 4012726c899SHans de Goede gc0310_remove(client); 4022726c899SHans de Goede return ret; 4032726c899SHans de Goede } 404ad85094bSMauro Carvalho Chehab 405ad85094bSMauro Carvalho Chehab dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 406ad85094bSMauro Carvalho Chehab dev->pad.flags = MEDIA_PAD_FL_SOURCE; 407ad85094bSMauro Carvalho Chehab dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 408ef5fb5d4SHans de Goede 409ef5fb5d4SHans de Goede ret = gc0310_init_controls(dev); 410ad85094bSMauro Carvalho Chehab if (ret) { 411ad85094bSMauro Carvalho Chehab gc0310_remove(client); 412ad85094bSMauro Carvalho Chehab return ret; 413ad85094bSMauro Carvalho Chehab } 414ad85094bSMauro Carvalho Chehab 415ad85094bSMauro Carvalho Chehab ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); 4162ec5bfe0SHans de Goede if (ret) { 417ad85094bSMauro Carvalho Chehab gc0310_remove(client); 418ad85094bSMauro Carvalho Chehab return ret; 419ad85094bSMauro Carvalho Chehab } 420ad85094bSMauro Carvalho Chehab 4212ec5bfe0SHans de Goede ret = atomisp_register_sensor_no_gmin(&dev->sd, 1, ATOMISP_INPUT_FORMAT_RAW_8, 4222ec5bfe0SHans de Goede atomisp_bayer_order_grbg); 4232ec5bfe0SHans de Goede if (ret) { 4242ec5bfe0SHans de Goede gc0310_remove(client); 4252ec5bfe0SHans de Goede return ret; 4262ec5bfe0SHans de Goede } 4272ec5bfe0SHans de Goede 4282ec5bfe0SHans de Goede return 0; 4292ec5bfe0SHans de Goede } 4302ec5bfe0SHans de Goede 4312726c899SHans de Goede static int gc0310_suspend(struct device *dev) 4322726c899SHans de Goede { 4332726c899SHans de Goede struct v4l2_subdev *sd = dev_get_drvdata(dev); 4342ec5bfe0SHans de Goede struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); 4352726c899SHans de Goede 4362ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->powerdown, 1); 4372ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->reset, 1); 4382ec5bfe0SHans de Goede return 0; 4392726c899SHans de Goede } 4402726c899SHans de Goede 4412726c899SHans de Goede static int gc0310_resume(struct device *dev) 4422726c899SHans de Goede { 4432726c899SHans de Goede struct v4l2_subdev *sd = dev_get_drvdata(dev); 4442ec5bfe0SHans de Goede struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); 4452726c899SHans de Goede 4462ec5bfe0SHans de Goede usleep_range(10000, 15000); 4472ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->reset, 0); 4482ec5bfe0SHans de Goede usleep_range(10000, 15000); 4492ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->powerdown, 0); 4502ec5bfe0SHans de Goede 4512ec5bfe0SHans de Goede return 0; 4522726c899SHans de Goede } 4532726c899SHans de Goede 4542726c899SHans de Goede static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, gc0310_suspend, gc0310_resume, NULL); 4552726c899SHans de Goede 456ad85094bSMauro Carvalho Chehab static const struct acpi_device_id gc0310_acpi_match[] = { 457ad85094bSMauro Carvalho Chehab {"INT0310"}, 458ad85094bSMauro Carvalho Chehab {}, 459ad85094bSMauro Carvalho Chehab }; 460ad85094bSMauro Carvalho Chehab MODULE_DEVICE_TABLE(acpi, gc0310_acpi_match); 461ad85094bSMauro Carvalho Chehab 462ad85094bSMauro Carvalho Chehab static struct i2c_driver gc0310_driver = { 463ad85094bSMauro Carvalho Chehab .driver = { 464ad85094bSMauro Carvalho Chehab .name = "gc0310", 4652726c899SHans de Goede .pm = pm_sleep_ptr(&gc0310_pm_ops), 466ad85094bSMauro Carvalho Chehab .acpi_match_table = gc0310_acpi_match, 467ad85094bSMauro Carvalho Chehab }, 468ad85094bSMauro Carvalho Chehab .probe_new = gc0310_probe, 469ad85094bSMauro Carvalho Chehab .remove = gc0310_remove, 470ad85094bSMauro Carvalho Chehab }; 471ad85094bSMauro Carvalho Chehab module_i2c_driver(gc0310_driver); 472ad85094bSMauro Carvalho Chehab 473ad85094bSMauro Carvalho Chehab MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>"); 474ad85094bSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors"); 475ad85094bSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 476