/* * imx132.c - imx132 sensor driver * * Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved. * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvc_utilities.h" #ifdef CONFIG_DEBUG_FS #include #endif #define IMX132_SIZEOF_I2C_BUF 16 struct imx132_reg { u16 addr; u16 val; }; struct imx132_info { struct miscdevice miscdev_info; int mode; struct imx132_power_rail power; struct nvc_fuseid fuse_id; struct i2c_client *i2c_client; struct imx132_platform_data *pdata; atomic_t in_use; struct clk *mclk; #ifdef CONFIG_DEBUG_FS struct nvc_debugfs_info debugfs_info; #endif }; #define IMX132_TABLE_WAIT_MS 0 #define IMX132_TABLE_END 1 #define IMX132_WAIT_MS 5 #define IMX132_FUSE_ID_SIZE 7 #define IMX132_FUSE_ID_DELAY 5 static struct regulator *imx132_ext_reg1; static struct regulator *imx132_ext_reg2; static struct imx132_reg mode_1920x1080[] = { /* Stand by */ {0x0100, 0x00}, {0x0101, 0x03}, {IMX132_TABLE_WAIT_MS, IMX132_WAIT_MS}, /* global settings */ {0x3087, 0x53}, {0x308B, 0x5A}, {0x3094, 0x11}, {0x309D, 0xA4}, {0x30AA, 0x01}, {0x30C6, 0x00}, {0x30C7, 0x00}, {0x3118, 0x2F}, {0x312A, 0x00}, {0x312B, 0x0B}, {0x312C, 0x0B}, {0x312D, 0x13}, /* PLL Setting */ {0x0305, 0x02}, {0x0307, 0x42}, {0x30A4, 0x02}, {0x303C, 0x4B}, /* Mode Setting */ {0x0340, 0x04}, {0x0341, 0x92}, {0x0342, 0x08}, {0x0343, 0xC8}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x00}, {0x0347, 0x1C}, {0x0348, 0x07}, {0x0349, 0xB7}, {0x034A, 0x04}, {0x034B, 0x93}, {0x034C, 0x07}, {0x034D, 0xB8}, {0x034E, 0x04}, {0x034F, 0x78}, {0x0381, 0x01}, {0x0383, 0x01}, {0x0385, 0x01}, {0x0387, 0x01}, {0x303D, 0x10}, {0x303E, 0x5A}, {0x3040, 0x08}, {0x3041, 0x97}, {0x3048, 0x00}, {0x304C, 0x2F}, {0x304D, 0x02}, {0x3064, 0x92}, {0x306A, 0x10}, {0x309B, 0x00}, {0x309E, 0x41}, {0x30A0, 0x10}, {0x30A1, 0x0B}, {0x30B2, 0x00}, {0x30D5, 0x00}, {0x30D6, 0x00}, {0x30D7, 0x00}, {0x30D8, 0x00}, {0x30D9, 0x00}, {0x30DA, 0x00}, {0x30DB, 0x00}, {0x30DC, 0x00}, {0x30DD, 0x00}, {0x30DE, 0x00}, {0x3102, 0x0C}, {0x3103, 0x33}, {0x3104, 0x18}, {0x3105, 0x00}, {0x3106, 0x65}, {0x3107, 0x00}, {0x3108, 0x06}, {0x3109, 0x04}, {0x310A, 0x04}, {0x315C, 0x3D}, {0x315D, 0x3C}, {0x316E, 0x3E}, {0x316F, 0x3D}, {0x3301, 0x01}, {0x3304, 0x07}, {0x3305, 0x06}, {0x3306, 0x19}, {0x3307, 0x03}, {0x3308, 0x0F}, {0x3309, 0x07}, {0x330A, 0x0C}, {0x330B, 0x06}, {0x330C, 0x0B}, {0x330D, 0x07}, {0x330E, 0x03}, {0x3318, 0x61}, {0x3322, 0x09}, {0x3342, 0x00}, {0x3348, 0xE0}, /* Shutter gain Settings */ {0x0202, 0x04}, {0x0203, 0x33}, /* Streaming */ {0x0100, 0x01}, {IMX132_TABLE_WAIT_MS, IMX132_WAIT_MS}, {IMX132_TABLE_END, 0x00} }; static struct imx132_reg mode_1976x1200[] = { /* Stand by */ {0x0100, 0x00}, {0x0101, 0x03}, {IMX132_TABLE_WAIT_MS, IMX132_WAIT_MS}, /* global settings */ {0x3087, 0x53}, {0x308B, 0x5A}, {0x3094, 0x11}, {0x309D, 0xA4}, {0x30AA, 0x01}, {0x30C6, 0x00}, {0x30C7, 0x00}, {0x3118, 0x2F}, {0x312A, 0x00}, {0x312B, 0x0B}, {0x312C, 0x0B}, {0x312D, 0x13}, /* PLL Setting */ {0x0305, 0x02}, {0x0307, 0x21}, {0x30A4, 0x02}, {0x303C, 0x4B}, /* Mode Setting */ {0x0340, 0x04}, {0x0341, 0xCA}, {0x0342, 0x08}, {0x0343, 0xC8}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x00}, {0x0347, 0x00}, {0x0348, 0x07}, {0x0349, 0xB7}, {0x034A, 0x04}, {0x034B, 0xAF}, {0x034C, 0x07}, {0x034D, 0xB8}, {0x034E, 0x04}, {0x034F, 0xB0}, {0x0381, 0x01}, {0x0383, 0x01}, {0x0385, 0x01}, {0x0387, 0x01}, {0x303D, 0x10}, {0x303E, 0x4A}, {0x3040, 0x08}, {0x3041, 0x97}, {0x3048, 0x00}, {0x304C, 0x2F}, {0x304D, 0x02}, {0x3064, 0x92}, {0x306A, 0x10}, {0x309B, 0x00}, {0x309E, 0x41}, {0x30A0, 0x10}, {0x30A1, 0x0B}, {0x30B2, 0x00}, {0x30D5, 0x00}, {0x30D6, 0x00}, {0x30D7, 0x00}, {0x30D8, 0x00}, {0x30D9, 0x00}, {0x30DA, 0x00}, {0x30DB, 0x00}, {0x30DC, 0x00}, {0x30DD, 0x00}, {0x30DE, 0x00}, {0x3102, 0x0C}, {0x3103, 0x33}, {0x3104, 0x30}, {0x3105, 0x00}, {0x3106, 0xCA}, {0x3107, 0x00}, {0x3108, 0x06}, {0x3109, 0x04}, {0x310A, 0x04}, {0x315C, 0x3D}, {0x315D, 0x3C}, {0x316E, 0x3E}, {0x316F, 0x3D}, {0x3301, 0x00}, {0x3304, 0x07}, {0x3305, 0x06}, {0x3306, 0x19}, {0x3307, 0x03}, {0x3308, 0x0F}, {0x3309, 0x07}, {0x330A, 0x0C}, {0x330B, 0x06}, {0x330C, 0x0B}, {0x330D, 0x07}, {0x330E, 0x03}, {0x3318, 0x67}, {0x3322, 0x09}, {0x3342, 0x00}, {0x3348, 0xE0}, /* Shutter gain Settings */ {0x0202, 0x04}, {0x0203, 0x33}, /* Streaming */ {0x0100, 0x01}, {IMX132_TABLE_WAIT_MS, IMX132_WAIT_MS}, {IMX132_TABLE_END, 0x00} }; enum { IMX132_MODE_1920X1080, IMX132_MODE_1976X1200, }; static struct imx132_reg *mode_table[] = { [IMX132_MODE_1920X1080] = mode_1920x1080, [IMX132_MODE_1976X1200] = mode_1976x1200, }; static inline void msleep_range(unsigned int delay_base) { usleep_range(delay_base*1000, delay_base*1000+500); } static inline void imx132_get_frame_length_regs(struct imx132_reg *regs, u32 frame_length) { regs->addr = IMX132_FRAME_LEN_LINES_15_8; regs->val = (frame_length >> 8) & 0xff; (regs + 1)->addr = IMX132_FRAME_LEN_LINES_7_0; (regs + 1)->val = (frame_length) & 0xff; } static inline void imx132_get_coarse_time_regs(struct imx132_reg *regs, u32 coarse_time) { regs->addr = IMX132_COARSE_INTEGRATION_TIME_15_8; regs->val = (coarse_time >> 8) & 0xff; (regs + 1)->addr = IMX132_COARSE_INTEGRATION_TIME_7_0; (regs + 1)->val = (coarse_time) & 0xff; } static inline void imx132_get_gain_reg(struct imx132_reg *regs, u16 gain) { regs->addr = IMX132_ANA_GAIN_GLOBAL; regs->val = gain; } static int imx132_read_reg(struct i2c_client *client, u16 addr, u8 *val) { int err; struct i2c_msg msg[2]; unsigned char data[3]; if (!client->adapter) return -ENODEV; msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 2; msg[0].buf = data; /* high byte goes out first */ data[0] = (u8) (addr >> 8); data[1] = (u8) (addr & 0xff); msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].len = 1; msg[1].buf = data + 2; err = i2c_transfer(client->adapter, msg, 2); if (err != 2) return -EINVAL; *val = data[2]; return 0; } static int imx132_write_reg(struct i2c_client *client, u16 addr, u8 val) { int err; struct i2c_msg msg; unsigned char data[3]; if (!client->adapter) return -ENODEV; data[0] = (u8) (addr >> 8); data[1] = (u8) (addr & 0xff); data[2] = (u8) (val & 0xff); msg.addr = client->addr; msg.flags = 0; msg.len = 3; msg.buf = data; err = i2c_transfer(client->adapter, &msg, 1); if (err == 1) return 0; dev_err(&client->dev, "%s:i2c write failed, %x = %x\n", __func__, addr, val); return err; } static int imx132_i2c_wr_blk(struct i2c_client *client, u8 *buf, int len) { struct i2c_msg msg; msg.addr = client->addr; msg.flags = 0; msg.len = len; msg.buf = buf; if (i2c_transfer(client->adapter, &msg, 1) != 1) return -EIO; return 0; } static int imx132_write_table(struct i2c_client *client, const struct imx132_reg table[], const struct imx132_reg override_list[], int num_override_regs) { int err; u8 i2c_transfer_buf[IMX132_SIZEOF_I2C_BUF]; const struct imx132_reg *next; const struct imx132_reg *n_next; u8 *b_ptr = i2c_transfer_buf; u16 buf_count = 0; for (next = table; next->addr != IMX132_TABLE_END; next++) { if (next->addr == IMX132_TABLE_WAIT_MS) { msleep_range(next->val); continue; } if (!buf_count) { b_ptr = i2c_transfer_buf; *b_ptr++ = next->addr >> 8; *b_ptr++ = next->addr & 0xFF; buf_count = 2; } *b_ptr++ = next->val; buf_count++; n_next = next + 1; if ((n_next->addr == next->addr + 1) && (n_next->addr != IMX132_TABLE_WAIT_MS) && (buf_count < IMX132_SIZEOF_I2C_BUF) && (n_next->addr != IMX132_TABLE_END)) continue; err = imx132_i2c_wr_blk(client, i2c_transfer_buf, buf_count); if (err) { pr_err("%s:imx132_write_table:%d", __func__, err); return err; } buf_count = 0; } return 0; } static int imx132_set_mode(struct imx132_info *info, struct imx132_mode *mode) { struct device *dev = &info->i2c_client->dev; int sensor_mode; int err; struct imx132_reg reg_list[5]; dev_info(dev, "%s: res [%ux%u] framelen %u coarsetime %u gain %u\n", __func__, mode->xres, mode->yres, mode->frame_length, mode->coarse_time, mode->gain); if ((mode->xres == 1920) && (mode->yres == 1080)) { sensor_mode = IMX132_MODE_1920X1080; } else if ((mode->xres == 1976) && (mode->yres == 1200)) { sensor_mode = IMX132_MODE_1976X1200; } else { dev_err(dev, "%s: invalid resolution to set mode %d %d\n", __func__, mode->xres, mode->yres); return -EINVAL; } /* * get a list of override regs for the asking frame length, * coarse integration time, and gain. */ imx132_get_frame_length_regs(reg_list, mode->frame_length); imx132_get_coarse_time_regs(reg_list + 2, mode->coarse_time); imx132_get_gain_reg(reg_list + 4, mode->gain); err = imx132_write_table(info->i2c_client, mode_table[sensor_mode], reg_list, 5); if (err) return err; info->mode = sensor_mode; dev_info(dev, "[imx132]: stream on.\n"); return 0; } static int imx132_get_status(struct imx132_info *info, u8 *dev_status) { /* TBD */ *dev_status = 0; return 0; } static int imx132_set_frame_length(struct imx132_info *info, u32 frame_length, bool group_hold) { struct imx132_reg reg_list[2]; int i = 0; int ret; imx132_get_frame_length_regs(reg_list, frame_length); if (group_hold) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x01); if (ret) return ret; } for (i = 0; i < NUM_OF_FRAME_LEN_REG; i++) { ret = imx132_write_reg(info->i2c_client, reg_list[i].addr, reg_list[i].val); if (ret) return ret; } if (group_hold) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x0); if (ret) return ret; } return 0; } static int imx132_set_coarse_time(struct imx132_info *info, u32 coarse_time, bool group_hold) { int ret; struct imx132_reg reg_list[2]; int i = 0; imx132_get_coarse_time_regs(reg_list, coarse_time); if (group_hold) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x01); if (ret) return ret; } for (i = 0; i < NUM_OF_COARSE_TIME_REG; i++) { ret = imx132_write_reg(info->i2c_client, reg_list[i].addr, reg_list[i].val); if (ret) return ret; } if (group_hold) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x0); if (ret) return ret; } return 0; } static int imx132_set_gain(struct imx132_info *info, u16 gain, bool group_hold) { int ret; struct imx132_reg reg_list; imx132_get_gain_reg(®_list, gain); if (group_hold) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x1); if (ret) return ret; } ret = imx132_write_reg(info->i2c_client, reg_list.addr, reg_list.val); if (ret) return ret; if (group_hold) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x0); if (ret) return ret; } return 0; } static int imx132_set_group_hold(struct imx132_info *info, struct imx132_ae *ae) { int ret; int count = 0; bool groupHoldEnabled = false; if (ae->gain_enable) count++; if (ae->coarse_time_enable) count++; if (ae->frame_length_enable) count++; if (count >= 2) groupHoldEnabled = true; if (groupHoldEnabled) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x1); if (ret) return ret; } if (ae->gain_enable) imx132_set_gain(info, ae->gain, false); if (ae->coarse_time_enable) imx132_set_coarse_time(info, ae->coarse_time, false); if (ae->frame_length_enable) imx132_set_frame_length(info, ae->frame_length, false); if (groupHoldEnabled) { ret = imx132_write_reg(info->i2c_client, IMX132_GROUP_PARAM_HOLD, 0x0); if (ret) return ret; } return 0; } static int imx132_get_fuse_id(struct imx132_info *info) { int ret = 0; int i; u8 bak = 0; if (info->fuse_id.size) return 0; /* * TBD 1: If the sensor does not have power at this point * Need to supply the power, e.g. by calling power on function */ msleep_range(IMX132_FUSE_ID_DELAY); for (i = 0; i < IMX132_FUSE_ID_SIZE ; i++) { ret |= imx132_read_reg(info->i2c_client, IMX132_FUSE_ID_REG + i, &bak); info->fuse_id.data[i] = bak; } if (!ret) info->fuse_id.size = i; return ret; } static long imx132_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int err; struct imx132_info *info = file->private_data; struct device *dev = &info->i2c_client->dev; switch (_IOC_NR(cmd)) { case _IOC_NR(IMX132_IOCTL_SET_MODE): { struct imx132_mode mode; if (copy_from_user(&mode, (const void __user *)arg, sizeof(struct imx132_mode))) { dev_err(dev, "%s:Failed to get mode from user.\n", __func__); return -EFAULT; } return imx132_set_mode(info, &mode); } case _IOC_NR(IMX132_IOCTL_SET_FRAME_LENGTH): return imx132_set_frame_length(info, (u32)arg, true); case _IOC_NR(IMX132_IOCTL_SET_COARSE_TIME): return imx132_set_coarse_time(info, (u32)arg, true); case _IOC_NR(IMX132_IOCTL_SET_GAIN): return imx132_set_gain(info, (u16)arg, true); case _IOC_NR(IMX132_IOCTL_GET_STATUS): { u8 status; err = imx132_get_status(info, &status); if (err) return err; if (copy_to_user((void __user *)arg, &status, 1)) { dev_err(dev, "%s:Failed to copy status to user.\n", __func__); return -EFAULT; } return 0; } case _IOC_NR(IMX132_IOCTL_GET_FUSEID): { err = imx132_get_fuse_id(info); if (err) { dev_err(dev, "%s:Failed to get fuse id info.\n", __func__); return err; } if (copy_to_user((void __user *)arg, &info->fuse_id, sizeof(struct nvc_fuseid))) { dev_info(dev, "%s:Fail copy fuse id to user space\n", __func__); return -EFAULT; } return 0; } case _IOC_NR(IMX132_IOCTL_SET_GROUP_HOLD): { struct imx132_ae ae; if (copy_from_user(&ae, (const void __user *)arg, sizeof(struct imx132_ae))) { dev_info(dev, "%s:fail group hold\n", __func__); return -EFAULT; } return imx132_set_group_hold(info, &ae); } default: dev_err(dev, "%s:unknown cmd.\n", __func__); return -EINVAL; } return 0; } static int imx132_get_extra_regulators(void) { if (!imx132_ext_reg1) { imx132_ext_reg1 = regulator_get(NULL, "imx132_reg1"); if (WARN_ON(IS_ERR(imx132_ext_reg1))) { pr_err("%s: can't get regulator imx132_reg1: %ld\n", __func__, PTR_ERR(imx132_ext_reg1)); imx132_ext_reg1 = NULL; return -ENODEV; } } if (!imx132_ext_reg2) { imx132_ext_reg2 = regulator_get(NULL, "imx132_reg2"); if (unlikely(WARN_ON(IS_ERR(imx132_ext_reg2)))) { pr_err("%s: can't get regulator imx132_reg2: %ld\n", __func__, PTR_ERR(imx132_ext_reg2)); imx132_ext_reg2 = NULL; regulator_put(imx132_ext_reg1); return -ENODEV; } } return 0; } static void imx132_mclk_disable(struct imx132_info *info) { dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__); clk_disable_unprepare(info->mclk); } static int imx132_mclk_enable(struct imx132_info *info) { int err; unsigned long mclk_init_rate = 24000000; dev_dbg(&info->i2c_client->dev, "%s: enable MCLK with %lu Hz\n", __func__, mclk_init_rate); err = clk_set_rate(info->mclk, mclk_init_rate); if (!err) err = clk_prepare_enable(info->mclk); return err; } static int imx132_power_on(struct imx132_info *info) { int err; struct imx132_power_rail *pw = &info->power; unsigned int cam2_gpio = info->pdata->cam2_gpio; if (unlikely(WARN_ON(!pw || !pw->avdd || !pw->iovdd || !pw->dvdd))) return -EFAULT; if (info->pdata->ext_reg) { if (imx132_get_extra_regulators()) goto imx132_poweron_fail; err = regulator_enable(imx132_ext_reg1); if (unlikely(err)) goto imx132_i2c_fail; err = regulator_enable(imx132_ext_reg2); if (unlikely(err)) goto imx132_vcm_fail; } gpio_set_value(cam2_gpio, 0); err = regulator_enable(pw->avdd); if (unlikely(err)) goto imx132_avdd_fail; err = regulator_enable(pw->dvdd); if (unlikely(err)) goto imx132_dvdd_fail; err = regulator_enable(pw->iovdd); if (unlikely(err)) goto imx132_iovdd_fail; usleep_range(1, 2); gpio_set_value(cam2_gpio, 1); return 0; imx132_iovdd_fail: regulator_disable(pw->dvdd); imx132_dvdd_fail: regulator_disable(pw->avdd); imx132_avdd_fail: if (info->pdata->ext_reg) regulator_disable(imx132_ext_reg2); imx132_vcm_fail: if (info->pdata->ext_reg) regulator_disable(imx132_ext_reg1); imx132_i2c_fail: imx132_poweron_fail: pr_err("%s failed.\n", __func__); return -ENODEV; } static int imx132_power_off(struct imx132_info *info) { struct imx132_power_rail *pw = &info->power; unsigned int cam2_gpio = info->pdata->cam2_gpio; if (!info->i2c_client->dev.of_node) { if (info->pdata && info->pdata->power_off) info->pdata->power_off(&info->power); goto imx132_pwroff_end; } if (unlikely(WARN_ON(!pw || !pw->avdd || !pw->iovdd || !pw->dvdd))) return -EFAULT; gpio_set_value(cam2_gpio, 0); usleep_range(1, 2); regulator_disable(pw->iovdd); regulator_disable(pw->dvdd); regulator_disable(pw->avdd); if (info->pdata->ext_reg) { regulator_disable(imx132_ext_reg1); regulator_disable(imx132_ext_reg2); } imx132_pwroff_end: return 0; } static int imx132_open(struct inode *inode, struct file *file) { int err; struct miscdevice *miscdev = file->private_data; struct imx132_info *info; info = container_of(miscdev, struct imx132_info, miscdev_info); /* check if the device is in use */ if (atomic_xchg(&info->in_use, 1)) { dev_info(&info->i2c_client->dev, "%s:BUSY!\n", __func__); return -EBUSY; } file->private_data = info; err = imx132_mclk_enable(info); if (err < 0) return err; if (info->i2c_client->dev.of_node) { err = imx132_power_on(info); } else { if (info->pdata && info->pdata->power_on) err = info->pdata->power_on(&info->power); else { dev_err(&info->i2c_client->dev, "%s:no valid power_on function.\n", __func__); err = -EEXIST; } } if (err < 0) goto imx132_open_fail; return 0; imx132_open_fail: imx132_mclk_disable(info); return err; } static int imx132_release(struct inode *inode, struct file *file) { struct imx132_info *info = file->private_data; imx132_power_off(info); imx132_mclk_disable(info); file->private_data = NULL; /* warn if device is already released */ WARN_ON(!atomic_xchg(&info->in_use, 0)); return 0; } static int imx132_power_put(struct imx132_power_rail *pw) { if (likely(pw->dvdd)) regulator_put(pw->dvdd); if (likely(pw->avdd)) regulator_put(pw->avdd); if (likely(pw->iovdd)) regulator_put(pw->iovdd); if (likely(imx132_ext_reg1)) regulator_put(imx132_ext_reg1); if (likely(imx132_ext_reg2)) regulator_put(imx132_ext_reg2); pw->dvdd = NULL; pw->avdd = NULL; pw->iovdd = NULL; imx132_ext_reg1 = NULL; imx132_ext_reg2 = NULL; return 0; } static int imx132_regulator_get(struct imx132_info *info, struct regulator **vreg, char vreg_name[]) { struct regulator *reg = NULL; int err = 0; reg = regulator_get(&info->i2c_client->dev, vreg_name); if (unlikely(IS_ERR(reg))) { dev_err(&info->i2c_client->dev, "%s %s ERR: %d\n", __func__, vreg_name, (int)reg); err = PTR_ERR(reg); reg = NULL; } else dev_dbg(&info->i2c_client->dev, "%s: %s\n", __func__, vreg_name); *vreg = reg; return err; } static int imx132_power_get(struct imx132_info *info) { struct imx132_power_rail *pw = &info->power; imx132_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */ imx132_regulator_get(info, &pw->avdd, "vana_imx132"); /* analog 2.7v */ imx132_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */ return 0; } static const struct file_operations imx132_fileops = { .owner = THIS_MODULE, .open = imx132_open, .unlocked_ioctl = imx132_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = imx132_ioctl, #endif .release = imx132_release, }; static struct miscdevice imx132_device = { .minor = MISC_DYNAMIC_MINOR, .name = "imx132", .fops = &imx132_fileops, }; static struct of_device_id imx132_of_match[] = { { .compatible = "nvidia,imx132", }, { }, }; MODULE_DEVICE_TABLE(of, imx132_of_match); static struct imx132_platform_data *imx132_parse_dt(struct i2c_client *client) { struct device_node *np = client->dev.of_node; struct imx132_platform_data *board_info_pdata; const struct of_device_id *match; match = of_match_device(imx132_of_match, &client->dev); if (!match) { dev_err(&client->dev, "Failed to find matching dt id\n"); return NULL; } board_info_pdata = devm_kzalloc(&client->dev, sizeof(*board_info_pdata), GFP_KERNEL); if (!board_info_pdata) { dev_err(&client->dev, "Failed to allocate pdata\n"); return NULL; } board_info_pdata->cam2_gpio = of_get_named_gpio(np, "cam2_gpios", 0); board_info_pdata->ext_reg = of_property_read_bool(np, "nvidia,ext_reg"); return board_info_pdata; } static int imx132_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct imx132_info *info; const char *mclk_name; int err = 0; pr_info("[imx132]: probing sensor.\n"); info = devm_kzalloc(&client->dev, sizeof(struct imx132_info), GFP_KERNEL); if (!info) { pr_err("[imx132]:%s:Unable to allocate memory!\n", __func__); return -ENOMEM; } if (client->dev.of_node) info->pdata = imx132_parse_dt(client); else info->pdata = client->dev.platform_data; if (!info->pdata) { pr_err("[imx132]:%s:Unable to get platform data\n", __func__); return -EFAULT; } info->i2c_client = client; atomic_set(&info->in_use, 0); info->mode = -1; mclk_name = info->pdata->mclk_name ? info->pdata->mclk_name : "default_mclk"; info->mclk = devm_clk_get(&client->dev, mclk_name); if (IS_ERR(info->mclk)) { dev_err(&client->dev, "%s: unable to get clock %s\n", __func__, mclk_name); return PTR_ERR(info->mclk); } i2c_set_clientdata(client, info); imx132_power_get(info); memcpy(&info->miscdev_info, &imx132_device, sizeof(struct miscdevice)); err = misc_register(&info->miscdev_info); if (err) { imx132_power_put(&info->power); pr_err("[imx132]:%s:Unable to register misc device!\n", __func__); } #ifdef CONFIG_DEBUG_FS info->debugfs_info.name = imx132_device.name; info->debugfs_info.i2c_client = info->i2c_client; info->debugfs_info.i2c_addr_limit = 0xFFFF; info->debugfs_info.i2c_rd8 = imx132_read_reg; info->debugfs_info.i2c_wr8 = imx132_write_reg; nvc_debugfs_init(&(info->debugfs_info)); #endif return err; } static int imx132_remove(struct i2c_client *client) { struct imx132_info *info = i2c_get_clientdata(client); #ifdef CONFIG_DEBUG_FS nvc_debugfs_remove(&info->debugfs_info); #endif imx132_power_put(&info->power); misc_deregister(&imx132_device); return 0; } static const struct i2c_device_id imx132_id[] = { { "imx132", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, imx132_id); static struct i2c_driver imx132_i2c_driver = { .driver = { .name = "imx132", .owner = THIS_MODULE, }, .probe = imx132_probe, .remove = imx132_remove, .id_table = imx132_id, }; static int __init imx132_init(void) { pr_info("[imx132] sensor driver loading\n"); return i2c_add_driver(&imx132_i2c_driver); } static void __exit imx132_exit(void) { i2c_del_driver(&imx132_i2c_driver); } module_init(imx132_init); module_exit(imx132_exit);