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/module.h> 20ad85094bSMauro Carvalho Chehab #include <linux/types.h> 21ad85094bSMauro Carvalho Chehab #include <linux/kernel.h> 22ad85094bSMauro Carvalho Chehab #include <linux/mm.h> 23ad85094bSMauro Carvalho Chehab #include <linux/string.h> 24ad85094bSMauro Carvalho Chehab #include <linux/errno.h> 25ad85094bSMauro Carvalho Chehab #include <linux/init.h> 26ad85094bSMauro Carvalho Chehab #include <linux/kmod.h> 27ad85094bSMauro Carvalho Chehab #include <linux/device.h> 28ad85094bSMauro Carvalho Chehab #include <linux/delay.h> 29ad85094bSMauro Carvalho Chehab #include <linux/slab.h> 302ec5bfe0SHans de Goede #include <linux/gpio/consumer.h> 312ec5bfe0SHans de Goede #include <linux/gpio/machine.h> 32ad85094bSMauro Carvalho Chehab #include <linux/i2c.h> 33ad85094bSMauro Carvalho Chehab #include <linux/moduleparam.h> 342726c899SHans de Goede #include <linux/pm_runtime.h> 35ad85094bSMauro Carvalho Chehab #include <media/v4l2-device.h> 36ad85094bSMauro Carvalho Chehab #include <linux/io.h> 37ad85094bSMauro Carvalho Chehab #include "../include/linux/atomisp_gmin_platform.h" 38ad85094bSMauro Carvalho Chehab 39ad85094bSMauro Carvalho Chehab #include "gc0310.h" 40ad85094bSMauro Carvalho Chehab 41ad85094bSMauro Carvalho Chehab /* 42ad85094bSMauro Carvalho Chehab * gc0310_write_reg_array - Initializes a list of GC0310 registers 43ad85094bSMauro Carvalho Chehab * @client: i2c driver client structure 44ad85094bSMauro Carvalho Chehab * @reglist: list of registers to be written 45bfe06aeeSHans de Goede * @count: number of register, value pairs in the list 46ad85094bSMauro Carvalho Chehab */ 47ad85094bSMauro Carvalho Chehab static int gc0310_write_reg_array(struct i2c_client *client, 48bfe06aeeSHans de Goede const struct gc0310_reg *reglist, int count) 49ad85094bSMauro Carvalho Chehab { 50bfe06aeeSHans de Goede int i, err; 51ad85094bSMauro Carvalho Chehab 52bfe06aeeSHans de Goede for (i = 0; i < count; i++) { 5365e5ef2fSHans de Goede err = i2c_smbus_write_byte_data(client, reglist[i].reg, reglist[i].val); 5465e5ef2fSHans de Goede if (err) { 5565e5ef2fSHans de Goede dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d", 5665e5ef2fSHans de Goede reglist[i].val, reglist[i].reg, err); 57ad85094bSMauro Carvalho Chehab return err; 58ad85094bSMauro Carvalho Chehab } 5965e5ef2fSHans de Goede } 60ad85094bSMauro Carvalho Chehab 61e1a4b3a7SHans de Goede return 0; 62ad85094bSMauro Carvalho Chehab } 63bdfe0bebSMauro Carvalho Chehab 64ef5fb5d4SHans de Goede static int gc0310_exposure_set(struct gc0310_device *dev, u32 exp) 65ef5fb5d4SHans de Goede { 66ef5fb5d4SHans de Goede struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); 67ef5fb5d4SHans de Goede 68ef5fb5d4SHans de Goede return i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, exp); 69ef5fb5d4SHans de Goede } 70ef5fb5d4SHans de Goede 71ef5fb5d4SHans de Goede static int gc0310_gain_set(struct gc0310_device *dev, u32 gain) 72ef5fb5d4SHans de Goede { 73ef5fb5d4SHans de Goede struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); 74ef5fb5d4SHans de Goede u8 again, dgain; 75ef5fb5d4SHans de Goede int ret; 76ef5fb5d4SHans de Goede 77ef5fb5d4SHans de Goede /* Taken from original driver, this never sets dgain lower then 32? */ 78ef5fb5d4SHans de Goede 79ef5fb5d4SHans de Goede /* Change 0 - 95 to 32 - 127 */ 80ef5fb5d4SHans de Goede gain += 32; 81ef5fb5d4SHans de Goede 82ef5fb5d4SHans de Goede if (gain < 64) { 83ef5fb5d4SHans de Goede again = 0x0; /* sqrt(2) */ 84ef5fb5d4SHans de Goede dgain = gain; 85ef5fb5d4SHans de Goede } else { 86ef5fb5d4SHans de Goede again = 0x2; /* 2 * sqrt(2) */ 87ef5fb5d4SHans de Goede dgain = gain / 2; 88ef5fb5d4SHans de Goede } 89ef5fb5d4SHans de Goede 90ef5fb5d4SHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_AGC_ADJ, again); 91ef5fb5d4SHans de Goede if (ret) 92ef5fb5d4SHans de Goede return ret; 93ef5fb5d4SHans de Goede 94ef5fb5d4SHans de Goede return i2c_smbus_write_byte_data(client, GC0310_DGC_ADJ, dgain); 95ef5fb5d4SHans de Goede } 96ef5fb5d4SHans de Goede 97ad85094bSMauro Carvalho Chehab static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) 98ad85094bSMauro Carvalho Chehab { 99ef5fb5d4SHans de Goede struct gc0310_device *dev = 100ef5fb5d4SHans de Goede container_of(ctrl->handler, struct gc0310_device, ctrls.handler); 101ef5fb5d4SHans de Goede int ret; 102ef5fb5d4SHans de Goede 1032726c899SHans de Goede /* Only apply changes to the controls if the device is powered up */ 1042726c899SHans de Goede if (!pm_runtime_get_if_in_use(dev->sd.dev)) 105ef5fb5d4SHans de Goede return 0; 106ad85094bSMauro Carvalho Chehab 107ad85094bSMauro Carvalho Chehab switch (ctrl->id) { 108ef5fb5d4SHans de Goede case V4L2_CID_EXPOSURE: 109ef5fb5d4SHans de Goede ret = gc0310_exposure_set(dev, ctrl->val); 110ef5fb5d4SHans de Goede break; 111ef5fb5d4SHans de Goede case V4L2_CID_GAIN: 112ef5fb5d4SHans de Goede ret = gc0310_gain_set(dev, ctrl->val); 113ef5fb5d4SHans de Goede break; 114ad85094bSMauro Carvalho Chehab default: 115ad85094bSMauro Carvalho Chehab ret = -EINVAL; 116ef5fb5d4SHans de Goede break; 117ad85094bSMauro Carvalho Chehab } 118ef5fb5d4SHans de Goede 1192726c899SHans de Goede pm_runtime_put(dev->sd.dev); 120ad85094bSMauro Carvalho Chehab return ret; 121ad85094bSMauro Carvalho Chehab } 122ad85094bSMauro Carvalho Chehab 123ad85094bSMauro Carvalho Chehab static const struct v4l2_ctrl_ops ctrl_ops = { 124ad85094bSMauro Carvalho Chehab .s_ctrl = gc0310_s_ctrl, 125ad85094bSMauro Carvalho Chehab }; 126ad85094bSMauro Carvalho Chehab 1279783b96aSHans de Goede static struct v4l2_mbus_framefmt * 1289783b96aSHans de Goede gc0310_get_pad_format(struct gc0310_device *dev, 1299783b96aSHans de Goede struct v4l2_subdev_state *state, 1309783b96aSHans de Goede unsigned int pad, enum v4l2_subdev_format_whence which) 131ad85094bSMauro Carvalho Chehab { 1329783b96aSHans de Goede if (which == V4L2_SUBDEV_FORMAT_TRY) 1339783b96aSHans de Goede return v4l2_subdev_get_try_format(&dev->sd, state, pad); 134ad85094bSMauro Carvalho Chehab 1359783b96aSHans de Goede return &dev->mode.fmt; 136ad85094bSMauro Carvalho Chehab } 137ad85094bSMauro Carvalho Chehab 1389783b96aSHans de Goede /* The GC0310 currently only supports 1 fixed fmt */ 1399783b96aSHans de Goede static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt) 1409783b96aSHans de Goede { 1419783b96aSHans de Goede memset(fmt, 0, sizeof(*fmt)); 1429783b96aSHans de Goede fmt->width = GC0310_NATIVE_WIDTH; 1439783b96aSHans de Goede fmt->height = GC0310_NATIVE_HEIGHT; 1449783b96aSHans de Goede fmt->field = V4L2_FIELD_NONE; 1459783b96aSHans de Goede fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; 146ad85094bSMauro Carvalho Chehab } 147ad85094bSMauro Carvalho Chehab 148ad85094bSMauro Carvalho Chehab static int gc0310_set_fmt(struct v4l2_subdev *sd, 1490d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 150ad85094bSMauro Carvalho Chehab struct v4l2_subdev_format *format) 151ad85094bSMauro Carvalho Chehab { 1529783b96aSHans de Goede struct gc0310_device *dev = to_gc0310_sensor(sd); 1539783b96aSHans de Goede struct v4l2_mbus_framefmt *fmt; 154bdfe0bebSMauro Carvalho Chehab 1559783b96aSHans de Goede fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which); 1569783b96aSHans de Goede gc0310_fill_format(fmt); 157ad85094bSMauro Carvalho Chehab 1589783b96aSHans de Goede format->format = *fmt; 1599783b96aSHans de Goede return 0; 160ad85094bSMauro Carvalho Chehab } 161ad85094bSMauro Carvalho Chehab 162ad85094bSMauro Carvalho Chehab static int gc0310_get_fmt(struct v4l2_subdev *sd, 1630d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 164ad85094bSMauro Carvalho Chehab struct v4l2_subdev_format *format) 165ad85094bSMauro Carvalho Chehab { 166ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 1679783b96aSHans de Goede struct v4l2_mbus_framefmt *fmt; 168ad85094bSMauro Carvalho Chehab 1699783b96aSHans de Goede fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which); 1709783b96aSHans de Goede format->format = *fmt; 171ad85094bSMauro Carvalho Chehab return 0; 172ad85094bSMauro Carvalho Chehab } 173ad85094bSMauro Carvalho Chehab 174ad85094bSMauro Carvalho Chehab static int gc0310_detect(struct i2c_client *client) 175ad85094bSMauro Carvalho Chehab { 176ad85094bSMauro Carvalho Chehab struct i2c_adapter *adapter = client->adapter; 177ad85094bSMauro Carvalho Chehab int ret; 178ad85094bSMauro Carvalho Chehab 179ad85094bSMauro Carvalho Chehab if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) 180ad85094bSMauro Carvalho Chehab return -ENODEV; 181ad85094bSMauro Carvalho Chehab 18265e5ef2fSHans de Goede ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H); 18365e5ef2fSHans de Goede if (ret < 0) { 18465e5ef2fSHans de Goede dev_err(&client->dev, "read sensor_id failed: %d\n", ret); 185ad85094bSMauro Carvalho Chehab return -ENODEV; 186ad85094bSMauro Carvalho Chehab } 187ad85094bSMauro Carvalho Chehab 18865e5ef2fSHans de Goede dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret); 18965e5ef2fSHans de Goede 19065e5ef2fSHans de Goede if (ret != GC0310_ID) { 19165e5ef2fSHans de Goede dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n", 19265e5ef2fSHans de Goede ret, GC0310_ID); 193ad85094bSMauro Carvalho Chehab return -ENODEV; 194ad85094bSMauro Carvalho Chehab } 195ad85094bSMauro Carvalho Chehab 196ad85094bSMauro Carvalho Chehab dev_dbg(&client->dev, "detect gc0310 success\n"); 197ad85094bSMauro Carvalho Chehab 198ad85094bSMauro Carvalho Chehab return 0; 199ad85094bSMauro Carvalho Chehab } 200ad85094bSMauro Carvalho Chehab 201ad85094bSMauro Carvalho Chehab static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) 202ad85094bSMauro Carvalho Chehab { 203ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 204ad85094bSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 2052726c899SHans de Goede int ret = 0; 206ad85094bSMauro Carvalho Chehab 2072dfc978aSDeepak R Varma dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable); 208ad85094bSMauro Carvalho Chehab mutex_lock(&dev->input_lock); 209ad85094bSMauro Carvalho Chehab 2102726c899SHans de Goede if (dev->is_streaming == enable) { 2112726c899SHans de Goede dev_warn(&client->dev, "stream already %s\n", enable ? "started" : "stopped"); 212b6763b22SHans de Goede goto error_unlock; 2132726c899SHans de Goede } 2142726c899SHans de Goede 2152726c899SHans de Goede if (enable) { 2162726c899SHans de Goede ret = pm_runtime_get_sync(&client->dev); 2172726c899SHans de Goede if (ret < 0) 2182726c899SHans de Goede goto error_power_down; 219b6763b22SHans de Goede 2202ec5bfe0SHans de Goede msleep(100); 2212ec5bfe0SHans de Goede 222b6763b22SHans de Goede ret = gc0310_write_reg_array(client, gc0310_reset_register, 223b6763b22SHans de Goede ARRAY_SIZE(gc0310_reset_register)); 224b6763b22SHans de Goede if (ret) 225b6763b22SHans de Goede goto error_power_down; 226b6763b22SHans de Goede 227b6763b22SHans de Goede ret = gc0310_write_reg_array(client, gc0310_VGA_30fps, 228b6763b22SHans de Goede ARRAY_SIZE(gc0310_VGA_30fps)); 229b6763b22SHans de Goede if (ret) 230b6763b22SHans de Goede goto error_power_down; 231b6763b22SHans de Goede 232b6763b22SHans de Goede /* restore value of all ctrls */ 233b6763b22SHans de Goede ret = __v4l2_ctrl_handler_setup(&dev->ctrls.handler); 234b6763b22SHans de Goede if (ret) 235b6763b22SHans de Goede goto error_power_down; 236b6763b22SHans de Goede 237ad85094bSMauro Carvalho Chehab /* enable per frame MIPI and sensor ctrl reset */ 23865e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30); 2392b2297b1SHans de Goede if (ret) 240b6763b22SHans de Goede goto error_power_down; 241ad85094bSMauro Carvalho Chehab } 242ad85094bSMauro Carvalho Chehab 24365e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3); 2442b2297b1SHans de Goede if (ret) 245b6763b22SHans de Goede goto error_power_down; 246ad85094bSMauro Carvalho Chehab 24765e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM, 24865e5ef2fSHans de Goede enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING); 2492b2297b1SHans de Goede if (ret) 250b6763b22SHans de Goede goto error_power_down; 251ad85094bSMauro Carvalho Chehab 25265e5ef2fSHans de Goede ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0); 2532b2297b1SHans de Goede if (ret) 254b6763b22SHans de Goede goto error_power_down; 255b6763b22SHans de Goede 256b6763b22SHans de Goede if (!enable) 2572726c899SHans de Goede pm_runtime_put(&client->dev); 258ad85094bSMauro Carvalho Chehab 2592726c899SHans de Goede dev->is_streaming = enable; 260ad85094bSMauro Carvalho Chehab mutex_unlock(&dev->input_lock); 2612b2297b1SHans de Goede return 0; 2622b2297b1SHans de Goede 263b6763b22SHans de Goede error_power_down: 2642726c899SHans de Goede pm_runtime_put(&client->dev); 2652726c899SHans de Goede dev->is_streaming = false; 2662b2297b1SHans de Goede error_unlock: 2672b2297b1SHans de Goede mutex_unlock(&dev->input_lock); 268ad85094bSMauro Carvalho Chehab return ret; 269ad85094bSMauro Carvalho Chehab } 270ad85094bSMauro Carvalho Chehab 2712ec5bfe0SHans de Goede static int gc0310_s_config(struct v4l2_subdev *sd) 272ad85094bSMauro Carvalho Chehab { 273ad85094bSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 2742ec5bfe0SHans de Goede int ret; 275ad85094bSMauro Carvalho Chehab 2762726c899SHans de Goede ret = pm_runtime_get_sync(&client->dev); 2772ec5bfe0SHans de Goede if (ret >= 0) 278ad85094bSMauro Carvalho Chehab ret = gc0310_detect(client); 279ad85094bSMauro Carvalho Chehab 2802726c899SHans de Goede pm_runtime_put(&client->dev); 281ad85094bSMauro Carvalho Chehab return ret; 282ad85094bSMauro Carvalho Chehab } 283ad85094bSMauro Carvalho Chehab 284ad85094bSMauro Carvalho Chehab static int gc0310_g_frame_interval(struct v4l2_subdev *sd, 285ad85094bSMauro Carvalho Chehab struct v4l2_subdev_frame_interval *interval) 286ad85094bSMauro Carvalho Chehab { 287ad85094bSMauro Carvalho Chehab interval->interval.numerator = 1; 2889783b96aSHans de Goede interval->interval.denominator = GC0310_FPS; 289ad85094bSMauro Carvalho Chehab 290ad85094bSMauro Carvalho Chehab return 0; 291ad85094bSMauro Carvalho Chehab } 292ad85094bSMauro Carvalho Chehab 293ad85094bSMauro Carvalho Chehab static int gc0310_enum_mbus_code(struct v4l2_subdev *sd, 2940d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 295ad85094bSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 296ad85094bSMauro Carvalho Chehab { 2979783b96aSHans de Goede /* We support only a single format */ 2989783b96aSHans de Goede if (code->index) 299ad85094bSMauro Carvalho Chehab return -EINVAL; 300ad85094bSMauro Carvalho Chehab 301ad85094bSMauro Carvalho Chehab code->code = MEDIA_BUS_FMT_SGRBG8_1X8; 302ad85094bSMauro Carvalho Chehab return 0; 303ad85094bSMauro Carvalho Chehab } 304ad85094bSMauro Carvalho Chehab 305ad85094bSMauro Carvalho Chehab static int gc0310_enum_frame_size(struct v4l2_subdev *sd, 3060d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 307ad85094bSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 308ad85094bSMauro Carvalho Chehab { 3099783b96aSHans de Goede /* We support only a single resolution */ 3109783b96aSHans de Goede if (fse->index) 311ad85094bSMauro Carvalho Chehab return -EINVAL; 312ad85094bSMauro Carvalho Chehab 3139783b96aSHans de Goede fse->min_width = GC0310_NATIVE_WIDTH; 3149783b96aSHans de Goede fse->max_width = GC0310_NATIVE_WIDTH; 3159783b96aSHans de Goede fse->min_height = GC0310_NATIVE_HEIGHT; 3169783b96aSHans de Goede fse->max_height = GC0310_NATIVE_HEIGHT; 317ad85094bSMauro Carvalho Chehab 318ad85094bSMauro Carvalho Chehab return 0; 319ad85094bSMauro Carvalho Chehab } 320ad85094bSMauro Carvalho Chehab 321ad85094bSMauro Carvalho Chehab static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) 322ad85094bSMauro Carvalho Chehab { 3239783b96aSHans de Goede *frames = GC0310_SKIP_FRAMES; 324ad85094bSMauro Carvalho Chehab return 0; 325ad85094bSMauro Carvalho Chehab } 326ad85094bSMauro Carvalho Chehab 327ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = { 328ad85094bSMauro Carvalho Chehab .g_skip_frames = gc0310_g_skip_frames, 329ad85094bSMauro Carvalho Chehab }; 330ad85094bSMauro Carvalho Chehab 331ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops gc0310_video_ops = { 332ad85094bSMauro Carvalho Chehab .s_stream = gc0310_s_stream, 333ad85094bSMauro Carvalho Chehab .g_frame_interval = gc0310_g_frame_interval, 334ad85094bSMauro Carvalho Chehab }; 335ad85094bSMauro Carvalho Chehab 336ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { 337ad85094bSMauro Carvalho Chehab .enum_mbus_code = gc0310_enum_mbus_code, 338ad85094bSMauro Carvalho Chehab .enum_frame_size = gc0310_enum_frame_size, 339ad85094bSMauro Carvalho Chehab .get_fmt = gc0310_get_fmt, 340ad85094bSMauro Carvalho Chehab .set_fmt = gc0310_set_fmt, 341ad85094bSMauro Carvalho Chehab }; 342ad85094bSMauro Carvalho Chehab 343ad85094bSMauro Carvalho Chehab static const struct v4l2_subdev_ops gc0310_ops = { 344ad85094bSMauro Carvalho Chehab .video = &gc0310_video_ops, 345ad85094bSMauro Carvalho Chehab .pad = &gc0310_pad_ops, 346ad85094bSMauro Carvalho Chehab .sensor = &gc0310_sensor_ops, 347ad85094bSMauro Carvalho Chehab }; 348ad85094bSMauro Carvalho Chehab 349ef5fb5d4SHans de Goede static int gc0310_init_controls(struct gc0310_device *dev) 350ef5fb5d4SHans de Goede { 351ef5fb5d4SHans de Goede struct v4l2_ctrl_handler *hdl = &dev->ctrls.handler; 352ef5fb5d4SHans de Goede 353ef5fb5d4SHans de Goede v4l2_ctrl_handler_init(hdl, 2); 354ef5fb5d4SHans de Goede 355ef5fb5d4SHans de Goede /* Use the same lock for controls as for everything else */ 356ef5fb5d4SHans de Goede hdl->lock = &dev->input_lock; 357ef5fb5d4SHans de Goede dev->sd.ctrl_handler = hdl; 358ef5fb5d4SHans de Goede 359ef5fb5d4SHans de Goede dev->ctrls.exposure = 360ef5fb5d4SHans de Goede v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023); 361ef5fb5d4SHans de Goede 362ef5fb5d4SHans de Goede /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ 363ef5fb5d4SHans de Goede dev->ctrls.gain = 364ef5fb5d4SHans de Goede v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31); 365ef5fb5d4SHans de Goede 366ef5fb5d4SHans de Goede return hdl->error; 367ef5fb5d4SHans de Goede } 368ef5fb5d4SHans de Goede 369ed5c2f5fSUwe Kleine-König static void gc0310_remove(struct i2c_client *client) 370ad85094bSMauro Carvalho Chehab { 371ad85094bSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 372ad85094bSMauro Carvalho Chehab struct gc0310_device *dev = to_gc0310_sensor(sd); 373bdfe0bebSMauro Carvalho Chehab 374ad85094bSMauro Carvalho Chehab dev_dbg(&client->dev, "gc0310_remove...\n"); 375ad85094bSMauro Carvalho Chehab 3762ec5bfe0SHans de Goede atomisp_unregister_subdev(sd); 377ad85094bSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 378ad85094bSMauro Carvalho Chehab media_entity_cleanup(&dev->sd.entity); 379ef5fb5d4SHans de Goede v4l2_ctrl_handler_free(&dev->ctrls.handler); 380*2746a966SHans de Goede mutex_destroy(&dev->input_lock); 3812726c899SHans de Goede pm_runtime_disable(&client->dev); 382ad85094bSMauro Carvalho Chehab } 383ad85094bSMauro Carvalho Chehab 384ad85094bSMauro Carvalho Chehab static int gc0310_probe(struct i2c_client *client) 385ad85094bSMauro Carvalho Chehab { 386ad85094bSMauro Carvalho Chehab struct gc0310_device *dev; 387ad85094bSMauro Carvalho Chehab int ret; 388c03496b3SMauro Carvalho Chehab 389340b4dd6SHans de Goede dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); 390ad85094bSMauro Carvalho Chehab if (!dev) 391ad85094bSMauro Carvalho Chehab return -ENOMEM; 392ad85094bSMauro Carvalho Chehab 3932ec5bfe0SHans de Goede ret = v4l2_get_acpi_sensor_info(&client->dev, NULL); 3942ec5bfe0SHans de Goede if (ret) 3952ec5bfe0SHans de Goede return ret; 3962ec5bfe0SHans de Goede 3972ec5bfe0SHans de Goede dev->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); 3982ec5bfe0SHans de Goede if (IS_ERR(dev->reset)) 3992ec5bfe0SHans de Goede return dev_err_probe(&client->dev, PTR_ERR(dev->reset), 4002ec5bfe0SHans de Goede "getting reset GPIO\n"); 4012ec5bfe0SHans de Goede 4022ec5bfe0SHans de Goede dev->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); 4032ec5bfe0SHans de Goede if (IS_ERR(dev->powerdown)) 4042ec5bfe0SHans de Goede return dev_err_probe(&client->dev, PTR_ERR(dev->powerdown), 4052ec5bfe0SHans de Goede "getting powerdown GPIO\n"); 4062ec5bfe0SHans de Goede 407ad85094bSMauro Carvalho Chehab mutex_init(&dev->input_lock); 408bdfe0bebSMauro Carvalho Chehab v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops); 4099783b96aSHans de Goede gc0310_fill_format(&dev->mode.fmt); 410ad85094bSMauro Carvalho Chehab 4112726c899SHans de Goede pm_runtime_set_suspended(&client->dev); 4122726c899SHans de Goede pm_runtime_enable(&client->dev); 4132726c899SHans de Goede pm_runtime_set_autosuspend_delay(&client->dev, 1000); 4142726c899SHans de Goede pm_runtime_use_autosuspend(&client->dev); 4152726c899SHans de Goede 4162ec5bfe0SHans de Goede ret = gc0310_s_config(&dev->sd); 4172726c899SHans de Goede if (ret) { 4182726c899SHans de Goede gc0310_remove(client); 4192726c899SHans de Goede return ret; 4202726c899SHans de Goede } 421ad85094bSMauro Carvalho Chehab 422ad85094bSMauro Carvalho Chehab dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 423ad85094bSMauro Carvalho Chehab dev->pad.flags = MEDIA_PAD_FL_SOURCE; 424ad85094bSMauro Carvalho Chehab dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 425ef5fb5d4SHans de Goede 426ef5fb5d4SHans de Goede ret = gc0310_init_controls(dev); 427ad85094bSMauro Carvalho Chehab if (ret) { 428ad85094bSMauro Carvalho Chehab gc0310_remove(client); 429ad85094bSMauro Carvalho Chehab return ret; 430ad85094bSMauro Carvalho Chehab } 431ad85094bSMauro Carvalho Chehab 432ad85094bSMauro Carvalho Chehab ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); 4332ec5bfe0SHans de Goede if (ret) { 434ad85094bSMauro Carvalho Chehab gc0310_remove(client); 435ad85094bSMauro Carvalho Chehab return ret; 436ad85094bSMauro Carvalho Chehab } 437ad85094bSMauro Carvalho Chehab 4382ec5bfe0SHans de Goede ret = atomisp_register_sensor_no_gmin(&dev->sd, 1, ATOMISP_INPUT_FORMAT_RAW_8, 4392ec5bfe0SHans de Goede atomisp_bayer_order_grbg); 4402ec5bfe0SHans de Goede if (ret) { 4412ec5bfe0SHans de Goede gc0310_remove(client); 4422ec5bfe0SHans de Goede return ret; 4432ec5bfe0SHans de Goede } 4442ec5bfe0SHans de Goede 4452ec5bfe0SHans de Goede return 0; 4462ec5bfe0SHans de Goede } 4472ec5bfe0SHans de Goede 4482726c899SHans de Goede static int gc0310_suspend(struct device *dev) 4492726c899SHans de Goede { 4502726c899SHans de Goede struct v4l2_subdev *sd = dev_get_drvdata(dev); 4512ec5bfe0SHans de Goede struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); 4522726c899SHans de Goede 4532ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->powerdown, 1); 4542ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->reset, 1); 4552ec5bfe0SHans de Goede return 0; 4562726c899SHans de Goede } 4572726c899SHans de Goede 4582726c899SHans de Goede static int gc0310_resume(struct device *dev) 4592726c899SHans de Goede { 4602726c899SHans de Goede struct v4l2_subdev *sd = dev_get_drvdata(dev); 4612ec5bfe0SHans de Goede struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); 4622726c899SHans de Goede 4632ec5bfe0SHans de Goede usleep_range(10000, 15000); 4642ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->reset, 0); 4652ec5bfe0SHans de Goede usleep_range(10000, 15000); 4662ec5bfe0SHans de Goede gpiod_set_value_cansleep(gc0310_dev->powerdown, 0); 4672ec5bfe0SHans de Goede 4682ec5bfe0SHans de Goede return 0; 4692726c899SHans de Goede } 4702726c899SHans de Goede 4712726c899SHans de Goede static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, gc0310_suspend, gc0310_resume, NULL); 4722726c899SHans de Goede 473ad85094bSMauro Carvalho Chehab static const struct acpi_device_id gc0310_acpi_match[] = { 474ad85094bSMauro Carvalho Chehab {"INT0310"}, 475ad85094bSMauro Carvalho Chehab {}, 476ad85094bSMauro Carvalho Chehab }; 477ad85094bSMauro Carvalho Chehab MODULE_DEVICE_TABLE(acpi, gc0310_acpi_match); 478ad85094bSMauro Carvalho Chehab 479ad85094bSMauro Carvalho Chehab static struct i2c_driver gc0310_driver = { 480ad85094bSMauro Carvalho Chehab .driver = { 481ad85094bSMauro Carvalho Chehab .name = "gc0310", 4822726c899SHans de Goede .pm = pm_sleep_ptr(&gc0310_pm_ops), 483ad85094bSMauro Carvalho Chehab .acpi_match_table = gc0310_acpi_match, 484ad85094bSMauro Carvalho Chehab }, 485ad85094bSMauro Carvalho Chehab .probe_new = gc0310_probe, 486ad85094bSMauro Carvalho Chehab .remove = gc0310_remove, 487ad85094bSMauro Carvalho Chehab }; 488ad85094bSMauro Carvalho Chehab module_i2c_driver(gc0310_driver); 489ad85094bSMauro Carvalho Chehab 490ad85094bSMauro Carvalho Chehab MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>"); 491ad85094bSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors"); 492ad85094bSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 493