/* * Copyright 2005-2014 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2019 NXP */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! * @file adv7180.c * * @brief Analog Device ADV7180 video decoder functions * * @ingroup Camera */ #include #include #include #include #include #include #include #include #include #include #include #include "v4l2-int-device.h" #include "mxc_v4l2_capture.h" #define ADV7180_VOLTAGE_ANALOG 1800000 #define ADV7180_VOLTAGE_DIGITAL_CORE 1800000 #define ADV7180_VOLTAGE_DIGITAL_IO 3300000 #define ADV7180_VOLTAGE_PLL 1800000 static struct regulator *dvddio_regulator; static struct regulator *dvdd_regulator; static struct regulator *avdd_regulator; static struct regulator *pvdd_regulator; static int pwn_gpio; static int adv7180_probe(struct i2c_client *adapter, const struct i2c_device_id *id); static int adv7180_detach(struct i2c_client *client); #ifdef CONFIG_OF static const struct of_device_id adv7180_of_match[] = { { .compatible = "adv,adv7180", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, adv7180_of_match); #endif static const struct i2c_device_id adv7180_id[] = { {"adv7180", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, adv7180_id); static struct i2c_driver adv7180_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = "adv7180", #ifdef CONFIG_OF .of_match_table = of_match_ptr(adv7180_of_match), #endif }, .probe = adv7180_probe, .remove = adv7180_detach, .id_table = adv7180_id, }; /*! * Maintains the information on the current state of the sensor. */ struct sensor { struct sensor_data sen; v4l2_std_id std_id; } adv7180_data; /*! List of input video formats supported. The video formats is corresponding * with v4l2 id in video_fmt_t */ typedef enum { ADV7180_NTSC = 0, /*!< Locked on (M) NTSC video signal. */ ADV7180_PAL, /*!< (B, G, H, I, N)PAL video signal. */ ADV7180_NOT_LOCKED, /*!< Not locked on a signal. */ } video_fmt_idx; /*! Number of video standards supported (including 'not locked' signal). */ #define ADV7180_STD_MAX (ADV7180_PAL + 1) /*! Video format structure. */ typedef struct { int v4l2_id; /*!< Video for linux ID. */ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */ u16 raw_width; /*!< Raw width. */ u16 raw_height; /*!< Raw height. */ u16 active_width; /*!< Active width. */ u16 active_height; /*!< Active height. */ int frame_rate; /*!< Frame rate. */ } video_fmt_t; /*! Description of video formats supported. * * PAL: raw=720x625, active=720x576. * NTSC: raw=720x525, active=720x480. */ static video_fmt_t video_fmts[] = { { /*! NTSC */ .v4l2_id = V4L2_STD_NTSC, .name = "NTSC", .raw_width = 720, /* SENS_FRM_WIDTH */ .raw_height = 525, /* SENS_FRM_HEIGHT */ .active_width = 720, /* ACT_FRM_WIDTH plus 1 */ .active_height = 480, /* ACT_FRM_WIDTH plus 1 */ .frame_rate = 30, }, { /*! (B, G, H, I, N) PAL */ .v4l2_id = V4L2_STD_PAL, .name = "PAL", .raw_width = 720, .raw_height = 625, .active_width = 720, .active_height = 576, .frame_rate = 25, }, { /*! Unlocked standard */ .v4l2_id = V4L2_STD_ALL, .name = "Autodetect", .raw_width = 720, .raw_height = 625, .active_width = 720, .active_height = 576, .frame_rate = 0, }, }; /*!* Standard index of ADV7180. */ static video_fmt_idx video_idx = ADV7180_PAL; /*! @brief This mutex is used to provide mutual exclusion. * * Create a mutex that can be used to provide mutually exclusive * read/write access to the globally accessible data structures * and variables that were defined above. */ static DEFINE_MUTEX(mutex); #define IF_NAME "adv7180" #define ADV7180_INPUT_CTL 0x00 /* Input Control */ #define ADV7180_STATUS_1 0x10 /* Status #1 */ #define ADV7180_BRIGHTNESS 0x0a /* Brightness */ #define ADV7180_IDENT 0x11 /* IDENT */ #define ADV7180_VSYNC_FIELD_CTL_1 0x31 /* VSYNC Field Control #1 */ #define ADV7180_MANUAL_WIN_CTL 0x3d /* Manual Window Control */ #define ADV7180_SD_SATURATION_CB 0xe3 /* SD Saturation Cb */ #define ADV7180_SD_SATURATION_CR 0xe4 /* SD Saturation Cr */ #define ADV7180_PWR_MNG 0x0f /* Power Management */ /* supported controls */ /* This hasn't been fully implemented yet. * This is how it should work, though. */ static struct v4l2_queryctrl adv7180_qctrl[] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", .minimum = 0, /* check this value */ .maximum = 255, /* check this value */ .step = 1, /* check this value */ .default_value = 127, /* check this value */ .flags = 0, }, { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Saturation", .minimum = 0, /* check this value */ .maximum = 255, /* check this value */ .step = 0x1, /* check this value */ .default_value = 127, /* check this value */ .flags = 0, } }; static inline void adv7180_power_down(int enable) { gpio_set_value_cansleep(pwn_gpio, !enable); msleep(2); } static int adv7180_regulator_enable(struct device *dev) { int ret = 0; dvddio_regulator = devm_regulator_get(dev, "DOVDD"); if (!IS_ERR(dvddio_regulator)) { regulator_set_voltage(dvddio_regulator, ADV7180_VOLTAGE_DIGITAL_IO, ADV7180_VOLTAGE_DIGITAL_IO); ret = regulator_enable(dvddio_regulator); if (ret) { dev_err(dev, "set io voltage failed\n"); return ret; } else { dev_dbg(dev, "set io voltage ok\n"); } } else { dev_warn(dev, "cannot get io voltage\n"); } dvdd_regulator = devm_regulator_get(dev, "DVDD"); if (!IS_ERR(dvdd_regulator)) { regulator_set_voltage(dvdd_regulator, ADV7180_VOLTAGE_DIGITAL_CORE, ADV7180_VOLTAGE_DIGITAL_CORE); ret = regulator_enable(dvdd_regulator); if (ret) { dev_err(dev, "set core voltage failed\n"); return ret; } else { dev_dbg(dev, "set core voltage ok\n"); } } else { dev_warn(dev, "cannot get core voltage\n"); } avdd_regulator = devm_regulator_get(dev, "AVDD"); if (!IS_ERR(avdd_regulator)) { regulator_set_voltage(avdd_regulator, ADV7180_VOLTAGE_ANALOG, ADV7180_VOLTAGE_ANALOG); ret = regulator_enable(avdd_regulator); if (ret) { dev_err(dev, "set analog voltage failed\n"); return ret; } else { dev_dbg(dev, "set analog voltage ok\n"); } } else { dev_warn(dev, "cannot get analog voltage\n"); } pvdd_regulator = devm_regulator_get(dev, "PVDD"); if (!IS_ERR(pvdd_regulator)) { regulator_set_voltage(pvdd_regulator, ADV7180_VOLTAGE_PLL, ADV7180_VOLTAGE_PLL); ret = regulator_enable(pvdd_regulator); if (ret) { dev_err(dev, "set pll voltage failed\n"); return ret; } else { dev_dbg(dev, "set pll voltage ok\n"); } } else { dev_warn(dev, "cannot get pll voltage\n"); } return ret; } /*********************************************************************** * I2C transfert. ***********************************************************************/ /*! Read one register from a ADV7180 i2c slave device. * * @param *reg register in the device we wish to access. * * @return 0 if success, an error code otherwise. */ static inline int adv7180_read(u8 reg) { int val; val = i2c_smbus_read_byte_data(adv7180_data.sen.i2c_client, reg); if (val < 0) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "%s:read reg error: reg=%2x\n", __func__, reg); return -1; } return val; } /*! Write one register of a ADV7180 i2c slave device. * * @param *reg register in the device we wish to access. * * @return 0 if success, an error code otherwise. */ static int adv7180_write_reg(u8 reg, u8 val) { s32 ret; ret = i2c_smbus_write_byte_data(adv7180_data.sen.i2c_client, reg, val); if (ret < 0) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "%s:write reg error:reg=%2x,val=%2x\n", __func__, reg, val); return -1; } return 0; } /*********************************************************************** * mxc_v4l2_capture interface. ***********************************************************************/ /*! * Return attributes of current video standard. * Since this device autodetects the current standard, this function also * sets the values that need to be changed if the standard changes. * There is no set std equivalent function. * * @return None. */ static void adv7180_get_std(v4l2_std_id *std) { int status_1, standard, idx; bool locked; dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_get_std\n"); status_1 = adv7180_read(ADV7180_STATUS_1); locked = status_1 & 0x1; standard = status_1 & 0x70; mutex_lock(&mutex); *std = V4L2_STD_ALL; idx = ADV7180_NOT_LOCKED; if (locked) { if (standard == 0x40) { *std = V4L2_STD_PAL; idx = ADV7180_PAL; } else if (standard == 0) { *std = V4L2_STD_NTSC; idx = ADV7180_NTSC; } } mutex_unlock(&mutex); /* This assumes autodetect which this device uses. */ if (*std != adv7180_data.std_id) { video_idx = idx; adv7180_data.std_id = *std; adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width; adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height; } } /*********************************************************************** * IOCTL Functions from v4l2_int_ioctl_desc. ***********************************************************************/ /*! * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num * s: pointer to standard V4L2 device structure * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure * * Gets slave interface parameters. * Calculates the required xclk value to support the requested * clock parameters in p. This value is returned in the p * parameter. * * vidioc_int_g_ifparm returns platform-specific information about the * interface settings used by the sensor. * * Called on open. */ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_ifparm\n"); if (s == NULL) { pr_err(" ERROR!! no slave device set!\n"); return -1; } /* Initialize structure to 0s then set any non-0 values. */ memset(p, 0, sizeof(*p)); p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; p->u.bt656.nobt_hs_inv = 1; p->u.bt656.bt_sync_correct = 1; /* ADV7180 has a dedicated clock so no clock settings needed. */ return 0; } /*! * Sets the camera power. * * s pointer to the camera device * on if 1, power is to be turned on. 0 means power is to be turned off * * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num * @s: pointer to standard V4L2 device structure * @on: power state to which device is to be set * * Sets devices power state to requrested state, if possible. * This is called on open, close, suspend and resume. */ static int ioctl_s_power(struct v4l2_int_device *s, int on) { struct sensor *sensor = s->priv; dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_s_power\n"); if (on && !sensor->sen.on) { if (adv7180_write_reg(ADV7180_PWR_MNG, 0x04) != 0) return -EIO; /* * FIXME:Additional 400ms to wait the chip to be stable? * This is a workaround for preview scrolling issue. */ msleep(400); } else if (!on && sensor->sen.on) { if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0) return -EIO; } sensor->sen.on = on; return 0; } /*! * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl * @s: pointer to standard V4L2 device structure * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure * * Returns the sensor's video CAPTURE parameters. */ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) { struct sensor *sensor = s->priv; struct v4l2_captureparm *cparm = &a->parm.capture; dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_parm\n"); switch (a->type) { /* These are all the possible cases. */ case V4L2_BUF_TYPE_VIDEO_CAPTURE: pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); memset(a, 0, sizeof(*a)); a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cparm->capability = sensor->sen.streamcap.capability; cparm->timeperframe = sensor->sen.streamcap.timeperframe; cparm->capturemode = sensor->sen.streamcap.capturemode; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: break; default: pr_debug("ioctl_g_parm:type is unknown %d\n", a->type); break; } return 0; } /*! * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl * @s: pointer to standard V4L2 device structure * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure * * Configures the sensor to use the input parameters, if possible. If * not possible, reverts to the old parameters and returns the * appropriate error code. * * This driver cannot change these settings. */ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_parm\n"); switch (a->type) { /* These are all the possible cases. */ case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: break; default: pr_debug(" type is unknown - %d\n", a->type); break; } return 0; } /*! * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap * @s: pointer to standard V4L2 device structure * @f: pointer to standard V4L2 v4l2_format structure * * Returns the sensor's current pixel format in the v4l2_format * parameter. */ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { struct sensor *sensor = s->priv; dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_fmt_cap\n"); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: pr_debug(" Returning size of %dx%d\n", sensor->sen.pix.width, sensor->sen.pix.height); f->fmt.pix = sensor->sen.pix; break; case V4L2_BUF_TYPE_PRIVATE: { v4l2_std_id std; adv7180_get_std(&std); f->fmt.pix.pixelformat = (u32)std; } break; default: f->fmt.pix = sensor->sen.pix; break; } return 0; } /*! * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl * @s: pointer to standard V4L2 device structure * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure * * If the requested control is supported, returns the control information * from the video_control[] array. Otherwise, returns -EINVAL if the * control is not supported. */ static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc) { int i; dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_queryctrl\n"); for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++) if (qc->id && qc->id == adv7180_qctrl[i].id) { memcpy(qc, &(adv7180_qctrl[i]), sizeof(*qc)); return 0; } return -EINVAL; } /*! * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl * @s: pointer to standard V4L2 device structure * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure * * If the requested control is supported, returns the control's current * value from the video_control[] array. Otherwise, returns -EINVAL * if the control is not supported. */ static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) { int ret = 0; int sat = 0; dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n"); switch (vc->id) { case V4L2_CID_BRIGHTNESS: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_BRIGHTNESS\n"); adv7180_data.sen.brightness = adv7180_read(ADV7180_BRIGHTNESS); vc->value = adv7180_data.sen.brightness; break; case V4L2_CID_CONTRAST: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_CONTRAST\n"); vc->value = adv7180_data.sen.contrast; break; case V4L2_CID_SATURATION: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_SATURATION\n"); sat = adv7180_read(ADV7180_SD_SATURATION_CB); adv7180_data.sen.saturation = sat; vc->value = adv7180_data.sen.saturation; break; case V4L2_CID_HUE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_HUE\n"); vc->value = adv7180_data.sen.hue; break; case V4L2_CID_AUTO_WHITE_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_AUTO_WHITE_BALANCE\n"); break; case V4L2_CID_DO_WHITE_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_DO_WHITE_BALANCE\n"); break; case V4L2_CID_RED_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_RED_BALANCE\n"); vc->value = adv7180_data.sen.red; break; case V4L2_CID_BLUE_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_BLUE_BALANCE\n"); vc->value = adv7180_data.sen.blue; break; case V4L2_CID_GAMMA: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_GAMMA\n"); break; case V4L2_CID_EXPOSURE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_EXPOSURE\n"); vc->value = adv7180_data.sen.ae_mode; break; case V4L2_CID_AUTOGAIN: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_AUTOGAIN\n"); break; case V4L2_CID_GAIN: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_GAIN\n"); break; case V4L2_CID_HFLIP: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_HFLIP\n"); break; case V4L2_CID_VFLIP: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_VFLIP\n"); break; default: dev_dbg(&adv7180_data.sen.i2c_client->dev, " Default case\n"); vc->value = 0; ret = -EPERM; break; } return ret; } /*! * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl * @s: pointer to standard V4L2 device structure * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure * * If the requested control is supported, sets the control's current * value in HW (and updates the video_control[] array). Otherwise, * returns -EINVAL if the control is not supported. */ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) { int retval = 0; u8 tmp; dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n"); switch (vc->id) { case V4L2_CID_BRIGHTNESS: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_BRIGHTNESS\n"); tmp = vc->value; adv7180_write_reg(ADV7180_BRIGHTNESS, tmp); adv7180_data.sen.brightness = vc->value; break; case V4L2_CID_CONTRAST: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_CONTRAST\n"); break; case V4L2_CID_SATURATION: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_SATURATION\n"); tmp = vc->value; adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp); adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp); adv7180_data.sen.saturation = vc->value; break; case V4L2_CID_HUE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_HUE\n"); break; case V4L2_CID_AUTO_WHITE_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_AUTO_WHITE_BALANCE\n"); break; case V4L2_CID_DO_WHITE_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_DO_WHITE_BALANCE\n"); break; case V4L2_CID_RED_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_RED_BALANCE\n"); break; case V4L2_CID_BLUE_BALANCE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_BLUE_BALANCE\n"); break; case V4L2_CID_GAMMA: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_GAMMA\n"); break; case V4L2_CID_EXPOSURE: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_EXPOSURE\n"); break; case V4L2_CID_AUTOGAIN: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_AUTOGAIN\n"); break; case V4L2_CID_GAIN: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_GAIN\n"); break; case V4L2_CID_HFLIP: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_HFLIP\n"); break; case V4L2_CID_VFLIP: dev_dbg(&adv7180_data.sen.i2c_client->dev, " V4L2_CID_VFLIP\n"); break; default: dev_dbg(&adv7180_data.sen.i2c_client->dev, " Default case\n"); retval = -EPERM; break; } return retval; } /*! * ioctl_enum_framesizes - V4L2 sensor interface handler for * VIDIOC_ENUM_FRAMESIZES ioctl * @s: pointer to standard V4L2 device structure * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure * * Return 0 if successful, otherwise -EINVAL. */ static int ioctl_enum_framesizes(struct v4l2_int_device *s, struct v4l2_frmsizeenum *fsize) { if (fsize->index >= 1) return -EINVAL; fsize->discrete.width = video_fmts[video_idx].active_width; fsize->discrete.height = video_fmts[video_idx].active_height; return 0; } /*! * ioctl_enum_frameintervals - V4L2 sensor interface handler for * VIDIOC_ENUM_FRAMEINTERVALS ioctl * @s: pointer to standard V4L2 device structure * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure * * Return 0 if successful, otherwise -EINVAL. */ static int ioctl_enum_frameintervals(struct v4l2_int_device *s, struct v4l2_frmivalenum *fival) { video_fmt_t fmt; int i; if (fival->index != 0) return -EINVAL; for (i = 0; i < ARRAY_SIZE(video_fmts) - 1; i++) { fmt = video_fmts[i]; if (fival->width == fmt.active_width && fival->height == fmt.active_height) { fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; fival->discrete.numerator = 1; fival->discrete.denominator = fmt.frame_rate; return 0; } } return -EINVAL; } /*! * ioctl_g_chip_ident - V4L2 sensor interface handler for * VIDIOC_DBG_G_CHIP_IDENT ioctl * @s: pointer to standard V4L2 device structure * @id: pointer to int * * Return 0. */ static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) { ((struct v4l2_dbg_chip_ident *)id)->match.type = V4L2_CHIP_MATCH_I2C_DRIVER; strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "adv7180_decoder"); ((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_ADV7180; return 0; } /*! * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT * @s: pointer to standard V4L2 device structure */ static int ioctl_init(struct v4l2_int_device *s) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_init\n"); return 0; } /*! * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num * @s: pointer to standard V4L2 device structure * * Initialise the device when slave attaches to the master. */ static int ioctl_dev_init(struct v4l2_int_device *s) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_dev_init\n"); return 0; } /*! * This structure defines all the ioctls for this module. */ static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = { {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init}, /*! * Delinitialise the dev. at slave detach. * The complement of ioctl_dev_init. */ /* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power}, {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm}, /* {vidioc_int_g_needs_reset_num, (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */ /* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */ {vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init}, /*! * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. */ /* {vidioc_int_enum_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */ /*! * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. * This ioctl is used to negotiate the image capture size and * pixel format without actually making it take effect. */ /* {vidioc_int_try_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap}, /*! * If the requested format is supported, configures the HW to use that * format, returns error code if format not supported or HW can't be * correctly configured. */ /* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm}, {vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm}, {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func*)ioctl_queryctrl}, {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func*)ioctl_g_ctrl}, {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func*)ioctl_s_ctrl}, {vidioc_int_enum_framesizes_num, (v4l2_int_ioctl_func *)ioctl_enum_framesizes}, {vidioc_int_enum_frameintervals_num, (v4l2_int_ioctl_func *) ioctl_enum_frameintervals}, {vidioc_int_g_chip_ident_num, (v4l2_int_ioctl_func *)ioctl_g_chip_ident}, }; static struct v4l2_int_slave adv7180_slave = { .ioctls = adv7180_ioctl_desc, .num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc), }; static struct v4l2_int_device adv7180_int_device = { .module = THIS_MODULE, .name = "adv7180", .type = v4l2_int_type_slave, .u = { .slave = &adv7180_slave, }, }; /*********************************************************************** * I2C client and driver. ***********************************************************************/ /*! ADV7180 Reset function. * * @return None. */ static void adv7180_hard_reset(bool cvbs) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:adv7180_hard_reset\n"); if (cvbs) { /* Set CVBS input on AIN1 */ adv7180_write_reg(ADV7180_INPUT_CTL, 0x00); } else { /* * Set YPbPr input on AIN1,4,5 and normal * operations(autodection of all stds). */ adv7180_write_reg(ADV7180_INPUT_CTL, 0x09); } /* Datasheet recommends */ adv7180_write_reg(0x01, 0xc8); adv7180_write_reg(0x02, 0x04); adv7180_write_reg(0x03, 0x00); adv7180_write_reg(0x04, 0x45); adv7180_write_reg(0x05, 0x00); adv7180_write_reg(0x06, 0x02); adv7180_write_reg(0x07, 0x7F); adv7180_write_reg(0x08, 0x80); adv7180_write_reg(0x0A, 0x00); adv7180_write_reg(0x0B, 0x00); adv7180_write_reg(0x0C, 0x36); adv7180_write_reg(0x0D, 0x7C); adv7180_write_reg(0x0E, 0x00); adv7180_write_reg(0x0F, 0x00); adv7180_write_reg(0x13, 0x00); adv7180_write_reg(0x14, 0x12); adv7180_write_reg(0x15, 0x00); adv7180_write_reg(0x16, 0x00); adv7180_write_reg(0x17, 0x01); adv7180_write_reg(0x18, 0x93); adv7180_write_reg(0xF1, 0x19); adv7180_write_reg(0x1A, 0x00); adv7180_write_reg(0x1B, 0x00); adv7180_write_reg(0x1C, 0x00); adv7180_write_reg(0x1D, 0x40); adv7180_write_reg(0x1E, 0x00); adv7180_write_reg(0x1F, 0x00); adv7180_write_reg(0x20, 0x00); adv7180_write_reg(0x21, 0x00); adv7180_write_reg(0x22, 0x00); adv7180_write_reg(0x23, 0xC0); adv7180_write_reg(0x24, 0x00); adv7180_write_reg(0x25, 0x00); adv7180_write_reg(0x26, 0x00); adv7180_write_reg(0x27, 0x58); adv7180_write_reg(0x28, 0x00); adv7180_write_reg(0x29, 0x00); adv7180_write_reg(0x2A, 0x00); adv7180_write_reg(0x2B, 0xE1); adv7180_write_reg(0x2C, 0xAE); adv7180_write_reg(0x2D, 0xF4); adv7180_write_reg(0x2E, 0x00); adv7180_write_reg(0x2F, 0xF0); adv7180_write_reg(0x30, 0x00); adv7180_write_reg(0x31, 0x12); adv7180_write_reg(0x32, 0x41); adv7180_write_reg(0x33, 0x84); adv7180_write_reg(0x34, 0x00); adv7180_write_reg(0x35, 0x02); adv7180_write_reg(0x36, 0x00); adv7180_write_reg(0x37, 0x01); adv7180_write_reg(0x38, 0x80); adv7180_write_reg(0x39, 0xC0); adv7180_write_reg(0x3A, 0x10); adv7180_write_reg(0x3B, 0x05); adv7180_write_reg(0x3C, 0x58); adv7180_write_reg(0x3D, 0xB2); adv7180_write_reg(0x3E, 0x64); adv7180_write_reg(0x3F, 0xE4); adv7180_write_reg(0x40, 0x90); adv7180_write_reg(0x41, 0x01); adv7180_write_reg(0x42, 0x7E); adv7180_write_reg(0x43, 0xA4); adv7180_write_reg(0x44, 0xFF); adv7180_write_reg(0x45, 0xB6); adv7180_write_reg(0x46, 0x12); adv7180_write_reg(0x48, 0x00); adv7180_write_reg(0x49, 0x00); adv7180_write_reg(0x4A, 0x00); adv7180_write_reg(0x4B, 0x00); adv7180_write_reg(0x4C, 0x00); adv7180_write_reg(0x4D, 0xEF); adv7180_write_reg(0x4E, 0x08); adv7180_write_reg(0x4F, 0x08); adv7180_write_reg(0x50, 0x08); adv7180_write_reg(0x51, 0x24); adv7180_write_reg(0x52, 0x0B); adv7180_write_reg(0x53, 0x4E); adv7180_write_reg(0x54, 0x80); adv7180_write_reg(0x55, 0x00); adv7180_write_reg(0x56, 0x10); adv7180_write_reg(0x57, 0x00); adv7180_write_reg(0x58, 0x00); adv7180_write_reg(0x59, 0x00); adv7180_write_reg(0x5A, 0x00); adv7180_write_reg(0x5B, 0x00); adv7180_write_reg(0x5C, 0x00); adv7180_write_reg(0x5D, 0x00); adv7180_write_reg(0x5E, 0x00); adv7180_write_reg(0x5F, 0x00); adv7180_write_reg(0x60, 0x00); adv7180_write_reg(0x61, 0x00); adv7180_write_reg(0x62, 0x20); adv7180_write_reg(0x63, 0x00); adv7180_write_reg(0x64, 0x00); adv7180_write_reg(0x65, 0x00); adv7180_write_reg(0x66, 0x00); adv7180_write_reg(0x67, 0x03); adv7180_write_reg(0x68, 0x01); adv7180_write_reg(0x69, 0x00); adv7180_write_reg(0x6A, 0x00); adv7180_write_reg(0x6B, 0xC0); adv7180_write_reg(0x6C, 0x00); adv7180_write_reg(0x6D, 0x00); adv7180_write_reg(0x6E, 0x00); adv7180_write_reg(0x6F, 0x00); adv7180_write_reg(0x70, 0x00); adv7180_write_reg(0x71, 0x00); adv7180_write_reg(0x72, 0x00); adv7180_write_reg(0x73, 0x10); adv7180_write_reg(0x74, 0x04); adv7180_write_reg(0x75, 0x01); adv7180_write_reg(0x76, 0x00); adv7180_write_reg(0x77, 0x3F); adv7180_write_reg(0x78, 0xFF); adv7180_write_reg(0x79, 0xFF); adv7180_write_reg(0x7A, 0xFF); adv7180_write_reg(0x7B, 0x1E); adv7180_write_reg(0x7C, 0xC0); adv7180_write_reg(0x7D, 0x00); adv7180_write_reg(0x7E, 0x00); adv7180_write_reg(0x7F, 0x00); adv7180_write_reg(0x80, 0x00); adv7180_write_reg(0x81, 0xC0); adv7180_write_reg(0x82, 0x04); adv7180_write_reg(0x83, 0x00); adv7180_write_reg(0x84, 0x0C); adv7180_write_reg(0x85, 0x02); adv7180_write_reg(0x86, 0x03); adv7180_write_reg(0x87, 0x63); adv7180_write_reg(0x88, 0x5A); adv7180_write_reg(0x89, 0x08); adv7180_write_reg(0x8A, 0x10); adv7180_write_reg(0x8B, 0x00); adv7180_write_reg(0x8C, 0x40); adv7180_write_reg(0x8D, 0x00); adv7180_write_reg(0x8E, 0x40); adv7180_write_reg(0x8F, 0x00); adv7180_write_reg(0x90, 0x00); adv7180_write_reg(0x91, 0x50); adv7180_write_reg(0x92, 0x00); adv7180_write_reg(0x93, 0x00); adv7180_write_reg(0x94, 0x00); adv7180_write_reg(0x95, 0x00); adv7180_write_reg(0x96, 0x00); adv7180_write_reg(0x97, 0xF0); adv7180_write_reg(0x98, 0x00); adv7180_write_reg(0x99, 0x00); adv7180_write_reg(0x9A, 0x00); adv7180_write_reg(0x9B, 0x00); adv7180_write_reg(0x9C, 0x00); adv7180_write_reg(0x9D, 0x00); adv7180_write_reg(0x9E, 0x00); adv7180_write_reg(0x9F, 0x00); adv7180_write_reg(0xA0, 0x00); adv7180_write_reg(0xA1, 0x00); adv7180_write_reg(0xA2, 0x00); adv7180_write_reg(0xA3, 0x00); adv7180_write_reg(0xA4, 0x00); adv7180_write_reg(0xA5, 0x00); adv7180_write_reg(0xA6, 0x00); adv7180_write_reg(0xA7, 0x00); adv7180_write_reg(0xA8, 0x00); adv7180_write_reg(0xA9, 0x00); adv7180_write_reg(0xAA, 0x00); adv7180_write_reg(0xAB, 0x00); adv7180_write_reg(0xAC, 0x00); adv7180_write_reg(0xAD, 0x00); adv7180_write_reg(0xAE, 0x60); adv7180_write_reg(0xAF, 0x00); adv7180_write_reg(0xB0, 0x00); adv7180_write_reg(0xB1, 0x60); adv7180_write_reg(0xB2, 0x1C); adv7180_write_reg(0xB3, 0x54); adv7180_write_reg(0xB4, 0x00); adv7180_write_reg(0xB5, 0x00); adv7180_write_reg(0xB6, 0x00); adv7180_write_reg(0xB7, 0x13); adv7180_write_reg(0xB8, 0x03); adv7180_write_reg(0xB9, 0x33); adv7180_write_reg(0xBF, 0x02); adv7180_write_reg(0xC0, 0x00); adv7180_write_reg(0xC1, 0x00); adv7180_write_reg(0xC2, 0x00); adv7180_write_reg(0xC3, 0x00); adv7180_write_reg(0xC4, 0x00); adv7180_write_reg(0xC5, 0x81); adv7180_write_reg(0xC6, 0x00); adv7180_write_reg(0xC7, 0x00); adv7180_write_reg(0xC8, 0x00); adv7180_write_reg(0xC9, 0x04); adv7180_write_reg(0xCC, 0x69); adv7180_write_reg(0xCD, 0x00); adv7180_write_reg(0xCE, 0x01); adv7180_write_reg(0xCF, 0xB4); adv7180_write_reg(0xD0, 0x00); adv7180_write_reg(0xD1, 0x10); adv7180_write_reg(0xD2, 0xFF); adv7180_write_reg(0xD3, 0xFF); adv7180_write_reg(0xD4, 0x7F); adv7180_write_reg(0xD5, 0x7F); adv7180_write_reg(0xD6, 0x3E); adv7180_write_reg(0xD7, 0x08); adv7180_write_reg(0xD8, 0x3C); adv7180_write_reg(0xD9, 0x08); adv7180_write_reg(0xDA, 0x3C); adv7180_write_reg(0xDB, 0x9B); adv7180_write_reg(0xDC, 0xAC); adv7180_write_reg(0xDD, 0x4C); adv7180_write_reg(0xDE, 0x00); adv7180_write_reg(0xDF, 0x00); adv7180_write_reg(0xE0, 0x14); adv7180_write_reg(0xE1, 0x80); adv7180_write_reg(0xE2, 0x80); adv7180_write_reg(0xE3, 0x80); adv7180_write_reg(0xE4, 0x80); adv7180_write_reg(0xE5, 0x25); adv7180_write_reg(0xE6, 0x44); adv7180_write_reg(0xE7, 0x63); adv7180_write_reg(0xE8, 0x65); adv7180_write_reg(0xE9, 0x14); adv7180_write_reg(0xEA, 0x63); adv7180_write_reg(0xEB, 0x55); adv7180_write_reg(0xEC, 0x55); adv7180_write_reg(0xEE, 0x00); adv7180_write_reg(0xEF, 0x4A); adv7180_write_reg(0xF0, 0x44); adv7180_write_reg(0xF1, 0x0C); adv7180_write_reg(0xF2, 0x32); adv7180_write_reg(0xF3, 0x00); adv7180_write_reg(0xF4, 0x3F); adv7180_write_reg(0xF5, 0xE0); adv7180_write_reg(0xF6, 0x69); adv7180_write_reg(0xF7, 0x10); adv7180_write_reg(0xF8, 0x00); adv7180_write_reg(0xF9, 0x03); adv7180_write_reg(0xFA, 0xFA); adv7180_write_reg(0xFB, 0x40); } /*! ADV7180 I2C attach function. * * @param *adapter struct i2c_adapter *. * * @return Error code indicating success or failure. */ /*! * ADV7180 I2C probe function. * Function set in i2c_driver struct. * Called by insmod. * * @param *adapter I2C adapter descriptor. * * @return Error code indicating success or failure. */ static int adv7180_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rev_id; int ret = 0; u32 cvbs = true; struct pinctrl *pinctrl; struct device *dev = &client->dev; printk(KERN_ERR"DBG sensor data is at %p\n", &adv7180_data); pinctrl = devm_pinctrl_get_select_default(dev); if (IS_ERR(pinctrl)) { dev_err(dev, "setup pinctrl failed\n"); return PTR_ERR(pinctrl); } /* request power down pin */ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); if (!gpio_is_valid(pwn_gpio)) { dev_err(dev, "no sensor pwdn pin available\n"); return -ENODEV; } ret = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, "adv7180_pwdn"); if (ret < 0) { dev_err(dev, "no power pin available!\n"); return ret; } adv7180_regulator_enable(dev); adv7180_power_down(0); msleep(1); /* Set initial values for the sensor struct. */ memset(&adv7180_data, 0, sizeof(adv7180_data)); adv7180_data.sen.i2c_client = client; adv7180_data.sen.streamcap.timeperframe.denominator = 30; adv7180_data.sen.streamcap.timeperframe.numerator = 1; adv7180_data.std_id = V4L2_STD_ALL; video_idx = ADV7180_NOT_LOCKED; adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width; adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height; adv7180_data.sen.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */ adv7180_data.sen.pix.priv = 1; /* 1 is used to indicate TV in */ adv7180_data.sen.on = true; adv7180_data.sen.sensor_clk = devm_clk_get(dev, "csi_mclk"); if (IS_ERR(adv7180_data.sen.sensor_clk)) { dev_err(dev, "get mclk failed\n"); return PTR_ERR(adv7180_data.sen.sensor_clk); } ret = of_property_read_u32(dev->of_node, "mclk", &adv7180_data.sen.mclk); if (ret) { dev_err(dev, "mclk frequency is invalid\n"); return ret; } ret = of_property_read_u32( dev->of_node, "mclk_source", (u32 *) &(adv7180_data.sen.mclk_source)); if (ret) { dev_err(dev, "mclk_source invalid\n"); return ret; } ret = of_property_read_u32(dev->of_node, "csi_id", &(adv7180_data.sen.csi)); if (ret) { dev_err(dev, "csi_id invalid\n"); return ret; } clk_prepare_enable(adv7180_data.sen.sensor_clk); dev_dbg(&adv7180_data.sen.i2c_client->dev, "%s:adv7180 probe i2c address is 0x%02X\n", __func__, adv7180_data.sen.i2c_client->addr); /*! Read the revision ID of the tvin chip */ rev_id = adv7180_read(ADV7180_IDENT); dev_dbg(dev, "%s:Analog Device adv7%2X0 detected!\n", __func__, rev_id); ret = of_property_read_u32(dev->of_node, "cvbs", &(cvbs)); if (ret) { dev_err(dev, "cvbs setting is not found\n"); cvbs = true; } /*! ADV7180 initialization. */ adv7180_hard_reset(cvbs); pr_debug(" type is %d (expect %d)\n", adv7180_int_device.type, v4l2_int_type_slave); pr_debug(" num ioctls is %d\n", adv7180_int_device.u.slave->num_ioctls); /* This function attaches this structure to the /dev/video0 device. * The pointer in priv points to the adv7180_data structure here.*/ adv7180_int_device.priv = &adv7180_data; ret = v4l2_int_device_register(&adv7180_int_device); clk_disable_unprepare(adv7180_data.sen.sensor_clk); return ret; } /*! * ADV7180 I2C detach function. * Called on rmmod. * * @param *client struct i2c_client*. * * @return Error code indicating success or failure. */ static int adv7180_detach(struct i2c_client *client) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "%s:Removing %s video decoder @ 0x%02X from adapter %s\n", __func__, IF_NAME, client->addr << 1, client->adapter->name); /* Power down via i2c */ adv7180_write_reg(ADV7180_PWR_MNG, 0x24); if (dvddio_regulator) regulator_disable(dvddio_regulator); if (dvdd_regulator) regulator_disable(dvdd_regulator); if (avdd_regulator) regulator_disable(avdd_regulator); if (pvdd_regulator) regulator_disable(pvdd_regulator); v4l2_int_device_unregister(&adv7180_int_device); return 0; } /*! * ADV7180 init function. * Called on insmod. * * @return Error code indicating success or failure. */ static __init int adv7180_init(void) { u8 err = 0; pr_debug("In adv7180_init\n"); /* Tells the i2c driver what functions to call for this driver. */ err = i2c_add_driver(&adv7180_i2c_driver); if (err != 0) pr_err("%s:driver registration failed, error=%d\n", __func__, err); return err; } /*! * ADV7180 cleanup function. * Called on rmmod. * * @return Error code indicating success or failure. */ static void __exit adv7180_clean(void) { dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_clean\n"); i2c_del_driver(&adv7180_i2c_driver); } module_init(adv7180_init); module_exit(adv7180_clean); MODULE_AUTHOR("Freescale Semiconductor"); MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver"); MODULE_LICENSE("GPL");