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> 20*63558464SHans de Goede #include <linux/errno.h> 212ec5bfe0SHans de Goede #include <linux/gpio/consumer.h> 22ad85094bSMauro Carvalho Chehab #include <linux/i2c.h> 23*63558464SHans de Goede #include <linux/kernel.h> 24*63558464SHans de Goede #include <linux/module.h> 252726c899SHans de Goede #include <linux/pm_runtime.h> 26*63558464SHans de Goede #include <linux/string.h> 27*63558464SHans de Goede #include <linux/types.h> 28ad85094bSMauro Carvalho Chehab 29*63558464SHans de Goede #include <media/v4l2-device.h> 30*63558464SHans de Goede 31*63558464SHans 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 17565e5ef2fSHans de Goede ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H); 17665e5ef2fSHans de Goede if (ret < 0) { 17765e5ef2fSHans de Goede dev_err(&client->dev, "read sensor_id failed: %d\n", ret); 178ad85094bSMauro Carvalho Chehab return -ENODEV; 179ad85094bSMauro Carvalho Chehab } 180ad85094bSMauro Carvalho Chehab 18165e5ef2fSHans de Goede dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret); 18265e5ef2fSHans de Goede 18365e5ef2fSHans de Goede if (ret != GC0310_ID) { 18465e5ef2fSHans de Goede dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n", 18565e5ef2fSHans de Goede ret, GC0310_ID); 186ad85094bSMauro Carvalho Chehab return -ENODEV; 187ad85094bSMauro Carvalho Chehab } 188ad85094bSMauro Carvalho Chehab 189ad85094bSMauro Carvalho Chehab dev_dbg(&client->dev, "detect gc0310 success\n"); 190ad85094bSMauro Carvalho Chehab 191ad85094bSMauro Carvalho Chehab return 0; 192ad85094bSMauro Carvalho Chehab } 193ad85094bSMauro Carvalho Chehab 194ad85094bSMauro Carvalho Chehab static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) 195ad85094bSMauro Carvalho Chehab { 196ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 197ad85094bSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 1982726c899SHans de Goede int ret = 0; 199ad85094bSMauro Carvalho Chehab 2002dfc978aSDeepak R Varma dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable); 201ad85094bSMauro Carvalho Chehab mutex_lock(&dev->input_lock); 202ad85094bSMauro Carvalho Chehab 2032726c899SHans de Goede if (dev->is_streaming == enable) { 2042726c899SHans de Goede dev_warn(&client->dev, "stream already %s\n", enable ? "started" : "stopped"); 205b6763b22SHans de Goede goto error_unlock; 2062726c899SHans de Goede } 2072726c899SHans de Goede 2082726c899SHans de Goede if (enable) { 2092726c899SHans de Goede ret = pm_runtime_get_sync(&client->dev); 2102726c899SHans de Goede if (ret < 0) 2112726c899SHans de Goede goto error_power_down; 212b6763b22SHans de Goede 2132ec5bfe0SHans de Goede msleep(100); 2142ec5bfe0SHans de Goede 215b6763b22SHans de Goede ret = gc0310_write_reg_array(client, gc0310_reset_register, 216b6763b22SHans de Goede ARRAY_SIZE(gc0310_reset_register)); 217b6763b22SHans de Goede if (ret) 218b6763b22SHans de Goede goto error_power_down; 219b6763b22SHans de Goede 220b6763b22SHans de Goede ret = gc0310_write_reg_array(client, gc0310_VGA_30fps, 221b6763b22SHans de Goede ARRAY_SIZE(gc0310_VGA_30fps)); 222b6763b22SHans de Goede if (ret) 223b6763b22SHans de Goede goto error_power_down; 224b6763b22SHans de Goede 225b6763b22SHans de Goede /* restore value of all ctrls */ 226b6763b22SHans de Goede ret = __v4l2_ctrl_handler_setup(&dev->ctrls.handler); 227b6763b22SHans de Goede if (ret) 228b6763b22SHans de Goede goto error_power_down; 229b6763b22SHans de Goede 230ad85094bSMauro Carvalho Chehab /* enable per frame MIPI and sensor ctrl reset */ 23165e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30); 2322b2297b1SHans de Goede if (ret) 233b6763b22SHans de Goede goto error_power_down; 234ad85094bSMauro Carvalho Chehab } 235ad85094bSMauro Carvalho Chehab 23665e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3); 2372b2297b1SHans de Goede if (ret) 238b6763b22SHans de Goede goto error_power_down; 239ad85094bSMauro Carvalho Chehab 24065e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM, 24165e5ef2fSHans de Goede enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING); 2422b2297b1SHans de Goede if (ret) 243b6763b22SHans de Goede goto error_power_down; 244ad85094bSMauro Carvalho Chehab 24565e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0); 2462b2297b1SHans de Goede if (ret) 247b6763b22SHans de Goede goto error_power_down; 248b6763b22SHans de Goede 249b6763b22SHans de Goede if (!enable) 2502726c899SHans de Goede pm_runtime_put(&client->dev); 251ad85094bSMauro Carvalho Chehab 2522726c899SHans de Goede dev->is_streaming = enable; 253ad85094bSMauro Carvalho Chehab mutex_unlock(&dev->input_lock); 2542b2297b1SHans de Goede return 0; 2552b2297b1SHans de Goede 256b6763b22SHans de Goede error_power_down: 2572726c899SHans de Goede pm_runtime_put(&client->dev); 2582726c899SHans de Goede dev->is_streaming = false; 2592b2297b1SHans de Goede error_unlock: 2602b2297b1SHans de Goede mutex_unlock(&dev->input_lock); 261ad85094bSMauro Carvalho Chehab return ret; 262ad85094bSMauro Carvalho Chehab } 263ad85094bSMauro Carvalho Chehab 2642ec5bfe0SHans de Goede static int gc0310_s_config(struct v4l2_subdev *sd) 265ad85094bSMauro Carvalho Chehab { 266ad85094bSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 2672ec5bfe0SHans de Goede int ret; 268ad85094bSMauro Carvalho Chehab 2692726c899SHans de Goede ret = pm_runtime_get_sync(&client->dev); 2702ec5bfe0SHans de Goede if (ret >= 0) 271ad85094bSMauro Carvalho Chehab ret = gc0310_detect(client); 272ad85094bSMauro Carvalho Chehab 2732726c899SHans de Goede pm_runtime_put(&client->dev); 274ad85094bSMauro Carvalho Chehab return ret; 275ad85094bSMauro Carvalho Chehab } 276ad85094bSMauro Carvalho Chehab 277ad85094bSMauro Carvalho Chehab static int gc0310_g_frame_interval(struct v4l2_subdev *sd, 278ad85094bSMauro Carvalho Chehab struct v4l2_subdev_frame_interval *interval) 279ad85094bSMauro Carvalho Chehab { 280ad85094bSMauro Carvalho Chehab interval->interval.numerator = 1; 2819783b96aSHans de Goede interval->interval.denominator = GC0310_FPS; 282ad85094bSMauro Carvalho Chehab 283ad85094bSMauro Carvalho Chehab return 0; 284ad85094bSMauro Carvalho Chehab } 285ad85094bSMauro Carvalho Chehab 286ad85094bSMauro Carvalho Chehab static int gc0310_enum_mbus_code(struct v4l2_subdev *sd, 2870d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 288ad85094bSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 289ad85094bSMauro Carvalho Chehab { 2909783b96aSHans de Goede /* We support only a single format */ 2919783b96aSHans de Goede if (code->index) 292ad85094bSMauro Carvalho Chehab return -EINVAL; 293ad85094bSMauro Carvalho Chehab 294ad85094bSMauro Carvalho Chehab code->code = MEDIA_BUS_FMT_SGRBG8_1X8; 295ad85094bSMauro Carvalho Chehab return 0; 296ad85094bSMauro Carvalho Chehab } 297ad85094bSMauro Carvalho Chehab 298ad85094bSMauro Carvalho Chehab static int gc0310_enum_frame_size(struct v4l2_subdev *sd, 2990d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 300ad85094bSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 301ad85094bSMauro Carvalho Chehab { 3029783b96aSHans de Goede /* We support only a single resolution */ 3039783b96aSHans de Goede if (fse->index) 304ad85094bSMauro Carvalho Chehab return -EINVAL; 305ad85094bSMauro Carvalho Chehab 3069783b96aSHans de Goede fse->min_width = GC0310_NATIVE_WIDTH; 3079783b96aSHans de Goede fse->max_width = GC0310_NATIVE_WIDTH; 3089783b96aSHans de Goede fse->min_height = GC0310_NATIVE_HEIGHT; 3099783b96aSHans de Goede fse->max_height = GC0310_NATIVE_HEIGHT; 310ad85094bSMauro Carvalho Chehab 311ad85094bSMauro Carvalho Chehab return 0; 312ad85094bSMauro Carvalho Chehab } 313ad85094bSMauro Carvalho Chehab 314ad85094bSMauro Carvalho Chehab static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) 315ad85094bSMauro Carvalho Chehab { 3169783b96aSHans de Goede *frames = GC0310_SKIP_FRAMES; 317ad85094bSMauro Carvalho Chehab return 0; 318ad85094bSMauro Carvalho Chehab } 319ad85094bSMauro Carvalho Chehab 320ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = { 321ad85094bSMauro Carvalho Chehab .g_skip_frames = gc0310_g_skip_frames, 322ad85094bSMauro Carvalho Chehab }; 323ad85094bSMauro Carvalho Chehab 324ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops gc0310_video_ops = { 325ad85094bSMauro Carvalho Chehab .s_stream = gc0310_s_stream, 326ad85094bSMauro Carvalho Chehab .g_frame_interval = gc0310_g_frame_interval, 327ad85094bSMauro Carvalho Chehab }; 328ad85094bSMauro Carvalho Chehab 329ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { 330ad85094bSMauro Carvalho Chehab .enum_mbus_code = gc0310_enum_mbus_code, 331ad85094bSMauro Carvalho Chehab .enum_frame_size = gc0310_enum_frame_size, 332ad85094bSMauro Carvalho Chehab .get_fmt = gc0310_get_fmt, 333ad85094bSMauro Carvalho Chehab .set_fmt = gc0310_set_fmt, 334ad85094bSMauro Carvalho Chehab }; 335ad85094bSMauro Carvalho Chehab 336ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_ops gc0310_ops = { 337ad85094bSMauro Carvalho Chehab .video = &gc0310_video_ops, 338ad85094bSMauro Carvalho Chehab .pad = &gc0310_pad_ops, 339ad85094bSMauro Carvalho Chehab .sensor = &gc0310_sensor_ops, 340ad85094bSMauro Carvalho Chehab }; 341ad85094bSMauro Carvalho Chehab 342ef5fb5d4SHans de Goede static int gc0310_init_controls(struct gc0310_device *dev) 343ef5fb5d4SHans de Goede { 344ef5fb5d4SHans de Goede struct v4l2_ctrl_handler *hdl = &dev->ctrls.handler; 345ef5fb5d4SHans de Goede 346ef5fb5d4SHans de Goede v4l2_ctrl_handler_init(hdl, 2); 347ef5fb5d4SHans de Goede 348ef5fb5d4SHans de Goede /* Use the same lock for controls as for everything else */ 349ef5fb5d4SHans de Goede hdl->lock = &dev->input_lock; 350ef5fb5d4SHans de Goede dev->sd.ctrl_handler = hdl; 351ef5fb5d4SHans de Goede 352ef5fb5d4SHans de Goede dev->ctrls.exposure = 353ef5fb5d4SHans de Goede v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023); 354ef5fb5d4SHans de Goede 355ef5fb5d4SHans de Goede /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ 356ef5fb5d4SHans de Goede dev->ctrls.gain = 357ef5fb5d4SHans de Goede v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31); 358ef5fb5d4SHans de Goede 359ef5fb5d4SHans de Goede return hdl->error; 360ef5fb5d4SHans de Goede } 361ef5fb5d4SHans de Goede 362ed5c2f5fSUwe Kleine-König static void gc0310_remove(struct i2c_client *client) 363ad85094bSMauro Carvalho Chehab { 364ad85094bSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 365ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 366bdfe0bebSMauro Carvalho Chehab 367ad85094bSMauro Carvalho Chehab dev_dbg(&client->dev, "gc0310_remove...\n"); 368ad85094bSMauro Carvalho Chehab 3692ec5bfe0SHans de Goede atomisp_unregister_subdev(sd); 370ad85094bSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 371ad85094bSMauro Carvalho Chehab media_entity_cleanup(&dev->sd.entity); 372ef5fb5d4SHans de Goede v4l2_ctrl_handler_free(&dev->ctrls.handler); 3732746a966SHans de Goede mutex_destroy(&dev->input_lock); 3742726c899SHans de Goede pm_runtime_disable(&client->dev); 375ad85094bSMauro Carvalho Chehab } 376ad85094bSMauro Carvalho Chehab 377ad85094bSMauro Carvalho Chehab static int gc0310_probe(struct i2c_client *client) 378ad85094bSMauro Carvalho Chehab { 379ad85094bSMauro Carvalho Chehab struct gc0310_device *dev; 380ad85094bSMauro Carvalho Chehab int ret; 381c03496b3SMauro Carvalho Chehab 382340b4dd6SHans de Goede dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); 383ad85094bSMauro Carvalho Chehab if (!dev) 384ad85094bSMauro Carvalho Chehab return -ENOMEM; 385ad85094bSMauro Carvalho Chehab 3862ec5bfe0SHans de Goede ret = v4l2_get_acpi_sensor_info(&client->dev, NULL); 3872ec5bfe0SHans de Goede if (ret) 3882ec5bfe0SHans de Goede return ret; 3892ec5bfe0SHans de Goede 3902ec5bfe0SHans de Goede dev->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); 3912ec5bfe0SHans de Goede if (IS_ERR(dev->reset)) 3922ec5bfe0SHans de Goede return dev_err_probe(&client->dev, PTR_ERR(dev->reset), 3932ec5bfe0SHans de Goede "getting reset GPIO\n"); 3942ec5bfe0SHans de Goede 3952ec5bfe0SHans de Goede dev->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); 3962ec5bfe0SHans de Goede if (IS_ERR(dev->powerdown)) 3972ec5bfe0SHans de Goede return dev_err_probe(&client->dev, PTR_ERR(dev->powerdown), 3982ec5bfe0SHans de Goede "getting powerdown GPIO\n"); 3992ec5bfe0SHans de Goede 400ad85094bSMauro Carvalho Chehab mutex_init(&dev->input_lock); 401bdfe0bebSMauro Carvalho Chehab v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops); 4029783b96aSHans de Goede gc0310_fill_format(&dev->mode.fmt); 403ad85094bSMauro Carvalho Chehab 4042726c899SHans de Goede pm_runtime_set_suspended(&client->dev); 4052726c899SHans de Goede pm_runtime_enable(&client->dev); 4062726c899SHans de Goede pm_runtime_set_autosuspend_delay(&client->dev, 1000); 4072726c899SHans de Goede pm_runtime_use_autosuspend(&client->dev); 4082726c899SHans de Goede 4092ec5bfe0SHans de Goede ret = gc0310_s_config(&dev->sd); 4102726c899SHans de Goede if (ret) { 4112726c899SHans de Goede gc0310_remove(client); 4122726c899SHans de Goede return ret; 4132726c899SHans de Goede } 414ad85094bSMauro Carvalho Chehab 415ad85094bSMauro Carvalho Chehab dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 416ad85094bSMauro Carvalho Chehab dev->pad.flags = MEDIA_PAD_FL_SOURCE; 417ad85094bSMauro Carvalho Chehab dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 418ef5fb5d4SHans de Goede 419ef5fb5d4SHans de Goede ret = gc0310_init_controls(dev); 420ad85094bSMauro Carvalho Chehab if (ret) { 421ad85094bSMauro Carvalho Chehab gc0310_remove(client); 422ad85094bSMauro Carvalho Chehab return ret; 423ad85094bSMauro Carvalho Chehab } 424ad85094bSMauro Carvalho Chehab 425ad85094bSMauro Carvalho Chehab ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); 4262ec5bfe0SHans de Goede if (ret) { 427ad85094bSMauro Carvalho Chehab gc0310_remove(client); 428ad85094bSMauro Carvalho Chehab return ret; 429ad85094bSMauro Carvalho Chehab } 430ad85094bSMauro Carvalho Chehab 4312ec5bfe0SHans de Goede ret = atomisp_register_sensor_no_gmin(&dev->sd, 1, ATOMISP_INPUT_FORMAT_RAW_8, 4322ec5bfe0SHans de Goede atomisp_bayer_order_grbg); 4332ec5bfe0SHans de Goede if (ret) { 4342ec5bfe0SHans de Goede gc0310_remove(client); 4352ec5bfe0SHans de Goede return ret; 4362ec5bfe0SHans de Goede } 4372ec5bfe0SHans de Goede 4382ec5bfe0SHans de Goede return 0; 4392ec5bfe0SHans de Goede } 4402ec5bfe0SHans de Goede 4412726c899SHans de Goede static int gc0310_suspend(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 gpiod_set_value_cansleep(gc0310_dev->powerdown, 1); 4472ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->reset, 1); 4482ec5bfe0SHans de Goede return 0; 4492726c899SHans de Goede } 4502726c899SHans de Goede 4512726c899SHans de Goede static int gc0310_resume(struct device *dev) 4522726c899SHans de Goede { 4532726c899SHans de Goede struct v4l2_subdev *sd = dev_get_drvdata(dev); 4542ec5bfe0SHans de Goede struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); 4552726c899SHans de Goede 4562ec5bfe0SHans de Goede usleep_range(10000, 15000); 4572ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->reset, 0); 4582ec5bfe0SHans de Goede usleep_range(10000, 15000); 4592ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->powerdown, 0); 4602ec5bfe0SHans de Goede 4612ec5bfe0SHans de Goede return 0; 4622726c899SHans de Goede } 4632726c899SHans de Goede 4642726c899SHans de Goede static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, gc0310_suspend, gc0310_resume, NULL); 4652726c899SHans de Goede 466ad85094bSMauro Carvalho Chehab static const struct acpi_device_id gc0310_acpi_match[] = { 467ad85094bSMauro Carvalho Chehab {"INT0310"}, 468ad85094bSMauro Carvalho Chehab {}, 469ad85094bSMauro Carvalho Chehab }; 470ad85094bSMauro Carvalho Chehab MODULE_DEVICE_TABLE(acpi, gc0310_acpi_match); 471ad85094bSMauro Carvalho Chehab 472ad85094bSMauro Carvalho Chehab static struct i2c_driver gc0310_driver = { 473ad85094bSMauro Carvalho Chehab .driver = { 474ad85094bSMauro Carvalho Chehab .name = "gc0310", 4752726c899SHans de Goede .pm = pm_sleep_ptr(&gc0310_pm_ops), 476ad85094bSMauro Carvalho Chehab .acpi_match_table = gc0310_acpi_match, 477ad85094bSMauro Carvalho Chehab }, 478ad85094bSMauro Carvalho Chehab .probe_new = gc0310_probe, 479ad85094bSMauro Carvalho Chehab .remove = gc0310_remove, 480ad85094bSMauro Carvalho Chehab }; 481ad85094bSMauro Carvalho Chehab module_i2c_driver(gc0310_driver); 482ad85094bSMauro Carvalho Chehab 483ad85094bSMauro Carvalho Chehab MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>"); 484ad85094bSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors"); 485ad85094bSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 486