diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 521bbf1b29bc..1700447afe43 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -221,6 +221,18 @@ config VIDEO_ADV7842 To compile this driver as a module, choose M here: the module will be called adv7842. +config VIDEO_ADV7481 + tristate "Analog Devices ADV7481 decoder" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + ---help--- + Support for the Analog Devices ADV7481 video decoder. + + This is a Analog Devices Component/Graphics/SD Digitizer + with HDMI Receiver. + + To compile this driver as a module, choose M here: the + module will be called adv7481. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C @@ -725,6 +737,7 @@ endmenu menu "Sensors used on soc_camera driver" if SOC_CAMERA + source "drivers/media/i2c/soc_camera/Kconfig" endif diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 07db257abfc1..7fa84a019809 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o +obj-$(CONFIG_VIDEO_ADV7481) += adv7481.o obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o obj-$(CONFIG_VIDEO_ADV7511) += adv7511.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c new file mode 100644 index 000000000000..359a860fdabb --- /dev/null +++ b/drivers/media/i2c/adv7481.c @@ -0,0 +1,1729 @@ +/* + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adv7481_reg.h" + +#define DRIVER_NAME "adv7481" + +#define I2C_RW_DELAY 100 +#define I2C_SW_RST_DELAY 10000 +#define GPIO_HW_DELAY_LOW 100000 +#define GPIO_HW_DELAY_HI 10000 +#define SDP_MIN_SLEEP 5000 +#define SDP_MAX_SLEEP 6000 +#define SDP_NUM_TRIES 30 +#define LOCK_MIN_SLEEP 5000 +#define LOCK_MAX_SLEEP 6000 +#define LOCK_NUM_TRIES 20 + +#define ONE_MHZ_TO_HZ 1000000 + +struct adv7481_state { + /* Platform Data */ + struct adv7481_platform_data pdata; + + /* V4L2 Data */ + struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrl_hdl; + struct v4l2_dv_timings timings; + struct v4l2_ctrl *cable_det_ctrl; + + /* media entity controls */ + struct media_pad pad; + + struct workqueue_struct *work_queues; + struct mutex mutex; + + struct i2c_client *client; + struct i2c_client *i2c_csi_txa; + struct i2c_client *i2c_csi_txb; + struct i2c_client *i2c_hdmi; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_cp; + struct i2c_client *i2c_sdp; + struct i2c_client *i2c_rep; + + /* device status and Flags */ + int irq; + int device_num; + int powerup; + + /* routing configuration data */ + int csia_src; + int csib_src; + int mode; + + /* CSI configuration data */ + int tx_auto_params; + enum adv7481_mipi_lane tx_lanes; + + /* worker to handle interrupts */ + struct delayed_work irq_delayed_work; +}; + +struct adv7481_hdmi_params { + uint16_t pll_lock; + uint32_t tmds_freq; + uint16_t vert_lock; + uint16_t horz_lock; + uint16_t pix_rep; + uint16_t color_depth; +}; + +struct adv7481_vid_params { + uint32_t pix_clk; + uint16_t act_pix; + uint16_t act_lines; + uint16_t tot_pix; + uint16_t tot_lines; + uint32_t fr_rate; + uint16_t intrlcd; +}; + +const uint8_t adv7481_default_edid_data[] = { +/* Block 0 (EDID Base Block) */ +0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, +/* Vendor Identification */ +0x45, 0x23, 0xDD, 0xDD, 0x01, 0x01, 0x01, 0x01, +/* Week of Manufacture */ +0x1E, +/* Year of Manufacture */ +0x19, +/* EDID Structure Version and Revision */ +0x01, 0x03, +/* Display Parameters */ +0x80, 0x10, 0x09, 0x78, 0x0A, +/* Color characteristics */ +0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4C, +/* Established Timings */ +0x21, 0x08, 0x00, +/* Standard Timings */ +0x81, 0xC0, 0x81, 0x40, 0x3B, 0xC0, 0x3B, 0x40, +0x31, 0xC0, 0x31, 0x40, 0x01, 0x01, 0x01, 0x01, +/* Detailed Timings Block */ +0x01, 0x1D, 0x00, 0xBC, 0x52, 0xD0, 0x1E, 0x20, +0xB8, 0x28, 0x55, 0x40, 0xA0, 0x5A, 0x00, 0x00, +0x00, 0x1E, +/* Monitor Descriptor Block 2 */ +0x8C, 0x0A, 0xD0, 0xB4, 0x20, 0xE0, 0x14, 0x10, +0x12, 0x48, 0x3A, 0x00, 0xD8, 0xA2, 0x00, 0x00, +0x00, 0x1E, +/* Monitor Descriptor Block 3 */ +0x00, 0x00, 0x00, 0xFD, 0x00, 0x17, 0x4B, 0x0F, +0x46, 0x0F, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, +/* Monitor Descriptor Block 4 */ +0x00, 0x00, 0x00, 0xFC, 0x00, 0x54, 0x56, 0x0A, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, +/* Extension Flag CEA */ +0x01, +/* Checksum */ +0x5B, + +/* Block 1 (Extension Block) */ +/* Extension Header */ +0x02, 0x03, 0x1E, +/* Display supports */ +0x71, +/* Video Data Bock */ +0x48, 0x84, 0x13, 0x3C, 0x03, 0x02, 0x11, 0x12, +0x01, +/* HDMI VSDB */ +/* Deep color All, Max_TMDS_Clock = 150 MHz */ +0x68, 0x03, 0x0C, 0x00, 0x10, 0x00, 0x80, +/* hdmi_video_present=0, 3d_present=0 */ +0x1E, 0x00, +/* Audio Data Block */ +0x23, +0x09, 0x07, 0x07, /* LPCM, max 2 ch, 48k, 44.1k, 32k */ +/* Speaker Allocation Data Block */ +0x83, 0x01, 0x00, 0x00, +/* Detailed Timing Descriptor */ +0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, +0x6E, 0x28, 0x55, 0x00, 0xA0, 0x2A, 0x53, 0x00, +0x00, 0x1E, +/* Detailed Timing Descriptor */ +0x8C, 0x0A, 0xD0, 0xB4, 0x20, 0xE0, 0x14, 0x10, +0x12, 0x48, 0x3A, 0x00, 0xD8, 0xA2, 0x00, 0x00, +0x00, 0x1E, +/* Detailed Timing Descriptor */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, +/* Detailed Timing Descriptor */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, +/* Detailed Timing Descriptor */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, +/* DTD padding */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* Checksum */ +0xC6 +}; + +#define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data) + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &(container_of(ctrl->handler, + struct adv7481_state, ctrl_hdl)->sd); +} + +static inline struct adv7481_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7481_state, sd); +} + +/* I2C Rd/Rw Functions */ +static int adv7481_wr_byte(struct i2c_client *i2c_client, unsigned int reg, + unsigned int value) +{ + int ret; + + ret = i2c_smbus_write_byte_data(i2c_client, reg & 0xFF, value); + usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); + + return ret; +} + +static int adv7481_rd_byte(struct i2c_client *i2c_client, unsigned int reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(i2c_client, reg & 0xFF); + usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); + + return ret; +} + +static int adv7481_set_irq(struct adv7481_state *state) +{ + int ret = 0; + + ret = adv7481_wr_byte(state->client, IO_REG_PAD_CTRL_1_ADDR, + ADV_REG_SETFIELD(1, IO_PDN_INT2) | + ADV_REG_SETFIELD(1, IO_PDN_INT3) | + ADV_REG_SETFIELD(1, IO_INV_LLC) | + ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD)); + ret |= adv7481_wr_byte(state->client, IO_REG_INT1_CONF_ADDR, + ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR, + IO_INTRQ_DUR_SEL) | + ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL)); + ret |= adv7481_wr_byte(state->client, IO_REG_INT2_CONF_ADDR, + ADV_REG_SETFIELD(1, IO_CP_LOCK_UNLOCK_EDGE_SEL)); + ret |= adv7481_wr_byte(state->client, IO_REG_DATAPATH_INT_MASKB_ADDR, + ADV_REG_SETFIELD(1, IO_CP_LOCK_CP_MB1) | + ADV_REG_SETFIELD(1, IO_CP_UNLOCK_CP_MB1) | + ADV_REG_SETFIELD(1, IO_VMUTE_REQUEST_HDMI_MB1) | + ADV_REG_SETFIELD(1, IO_INT_SD_MB1)); + /* Set hpa */ + ret |= adv7481_wr_byte(state->client, IO_HDMI_LVL_INT_MASKB_3_ADDR, + ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1)); + + if (ret) + pr_err("%s: Failed %d to setup interrupt regs\n", + __func__, ret); + else + enable_irq(state->irq); + + return ret; +} + +static int adv7481_set_edid(struct adv7481_state *state) +{ + int i; + int ret = 0; + uint8_t edid_state; + + /* Enable Manual Control of EDID on Port A */ + ret |= adv7481_wr_byte(state->i2c_rep, 0x74, 0x01); + /* Disable Auto Enable of EDID */ + ret |= adv7481_wr_byte(state->i2c_rep, 0x7A, 0x08); + /* Set Primary EDID Size to 256 Bytes */ + ret |= adv7481_wr_byte(state->i2c_rep, 0x70, 0x20); + + /* + * Readback EDID enable state after a combination of manual + * and automatic functions + */ + edid_state = adv7481_rd_byte(state->i2c_rep, + HDMI_REG_RO_EDID_DEBUG_2_ADDR); + pr_debug("%s: Readback EDID enable state: 0x%x\n", __func__, + edid_state); + + for (i = 0; i < ADV7481_EDID_SIZE; i++) { + ret |= adv7481_wr_byte(state->i2c_edid, i, + adv7481_default_edid_data[i]); + } + + return ret; +} + +static irqreturn_t adv7481_irq(int irq, void *dev) +{ + struct adv7481_state *state = dev; + + schedule_delayed_work(&state->irq_delayed_work, + msecs_to_jiffies(0)); + return IRQ_HANDLED; +} + +static void adv7481_irq_delay_work(struct work_struct *work) +{ + struct adv7481_state *state; + uint8_t status; + + state = container_of(work, struct adv7481_state, + irq_delayed_work.work); + + mutex_lock(&state->mutex); + + /* workaround for irq trigger */ + status = adv7481_rd_byte(state->client, + IO_REG_INT_RAW_STATUS_ADDR); + + pr_debug("%s: dev: %d got int raw status: 0x%x\n", __func__, + state->device_num, status); + + status = adv7481_rd_byte(state->client, + IO_REG_DATAPATH_INT_STATUS_ADDR); + + pr_debug("%s: dev: %d got datapath int status: 0x%x\n", __func__, + state->device_num, status); + + adv7481_wr_byte(state->client, + IO_REG_DATAPATH_INT_CLEAR_ADDR, status); + + status = adv7481_rd_byte(state->client, + IO_REG_DATAPATH_RAW_STATUS_ADDR); + + pr_debug("%s: dev: %d got datapath rawstatus: 0x%x\n", __func__, + state->device_num, status); + + status = adv7481_rd_byte(state->client, + IO_HDMI_LVL_INT_STATUS_3_ADDR); + + pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", __func__, + state->device_num, status); + + adv7481_wr_byte(state->client, + IO_HDMI_LVL_INT_CLEAR_3_ADDR, status); + + mutex_unlock(&state->mutex); +} + +static int adv7481_cec_wakeup(struct adv7481_state *state, bool enable) +{ + uint8_t val; + int ret = 0; + + val = adv7481_rd_byte(state->client, + IO_REG_PWR_DN2_XTAL_HIGH_ADDR); + val = ADV_REG_GETFIELD(val, IO_PROG_XTAL_FREQ_HIGH); + if (enable) { + /* CEC wake up enabled in power-down mode */ + val |= ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | + ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDNB); + ret = adv7481_wr_byte(state->client, + IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); + } else { + /* CEC wake up disabled in power-down mode */ + val |= ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | + ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDNB); + ret = adv7481_wr_byte(state->client, + IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); + } + return ret; +} + +/* Initialize adv7481 I2C Settings */ +static int adv7481_dev_init(struct adv7481_state *state, + struct i2c_client *client) +{ + int ret; + + mutex_lock(&state->mutex); + + /* Soft reset */ + ret = adv7481_wr_byte(state->client, + IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); + /* Delay required following I2C reset and I2C transactions */ + usleep_range(I2C_SW_RST_DELAY, I2C_SW_RST_DELAY+1000); + + /* Disable CEC wake up in power-down mode */ + ret |= adv7481_cec_wakeup(state, 0); + /* Setting Vid_Std to 720x480p60 */ + ret |= adv7481_wr_byte(state->client, + IO_REG_CP_VID_STD_ADDR, 0x4A); + + /* Configure I2C Maps and I2C Communication Settings */ + /* io_reg_f2 I2C Auto Increment */ + ret |= adv7481_wr_byte(state->client, IO_REG_I2C_CFG_ADDR, + IO_REG_I2C_AUTOINC_EN_REG_VALUE); + /* DPLL Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_DPLL_ADDR, + IO_REG_DPLL_SADDR); + /* CP Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_CP_ADDR, + IO_REG_CP_SADDR); + /* HDMI RX Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_ADDR, + IO_REG_HDMI_SADDR); + /* EDID Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_EDID_ADDR, + IO_REG_EDID_SADDR); + /* HDMI RX Repeater Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_REP_ADDR, + IO_REG_HDMI_REP_SADDR); + /* HDMI RX Info-frame Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_INF_ADDR, + IO_REG_HDMI_INF_SADDR); + /* CBUS Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_CBUS_ADDR, + IO_REG_CBUS_SADDR); + /* CEC Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_CEC_ADDR, + IO_REG_CEC_SADDR); + /* SDP Main Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_SDP_ADDR, + IO_REG_SDP_SADDR); + /* CSI-TXB Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXB_ADDR, + IO_REG_CSI_TXB_SADDR); + /* CSI-TXA Map Address */ + ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXA_ADDR, + IO_REG_CSI_TXA_SADDR); + if (ret) { + pr_err("%s: Failed dev init %d\n", __func__, ret); + goto err_exit; + } + + /* Configure i2c clients */ + state->i2c_csi_txa = i2c_new_dummy(client->adapter, + IO_REG_CSI_TXA_SADDR >> 1); + state->i2c_csi_txb = i2c_new_dummy(client->adapter, + IO_REG_CSI_TXB_SADDR >> 1); + state->i2c_cp = i2c_new_dummy(client->adapter, + IO_REG_CP_SADDR >> 1); + state->i2c_hdmi = i2c_new_dummy(client->adapter, + IO_REG_HDMI_SADDR >> 1); + state->i2c_edid = i2c_new_dummy(client->adapter, + IO_REG_EDID_SADDR >> 1); + state->i2c_sdp = i2c_new_dummy(client->adapter, + IO_REG_SDP_SADDR >> 1); + state->i2c_rep = i2c_new_dummy(client->adapter, + IO_REG_HDMI_REP_SADDR >> 1); + + if (!state->i2c_csi_txa || !state->i2c_csi_txb || !state->i2c_cp || + !state->i2c_sdp || !state->i2c_hdmi || !state->i2c_edid || + !state->i2c_rep) { + pr_err("%s: Additional I2C Client Fail\n", __func__); + ret = -EFAULT; + goto err_exit; + } + + ret = adv7481_set_edid(state); + ret |= adv7481_set_irq(state); + +err_exit: + mutex_unlock(&state->mutex); + + return ret; +} + +/* Initialize adv7481 hardware */ +static int adv7481_hw_init(struct adv7481_platform_data *pdata, + struct adv7481_state *state) +{ + int ret = 0; + + if (!pdata) { + pr_err("%s: PDATA is NULL\n", __func__); + return -EFAULT; + } + + mutex_lock(&state->mutex); + + if (gpio_is_valid(pdata->rstb_gpio)) { + ret = gpio_request(pdata->rstb_gpio, "rstb_gpio"); + if (ret) { + pr_err("%s: Request GPIO Fail %d\n", __func__, ret); + goto err_exit; + } + ret = gpio_direction_output(pdata->rstb_gpio, 0); + usleep_range(GPIO_HW_DELAY_LOW, GPIO_HW_DELAY_LOW+1000); + ret = gpio_direction_output(pdata->rstb_gpio, 1); + usleep_range(GPIO_HW_DELAY_HI, GPIO_HW_DELAY_HI+1000); + if (ret) { + pr_err("%s: Set GPIO Fail %d\n", __func__, ret); + goto err_exit; + } + } + + /* Only setup IRQ1 for now... */ + if (gpio_is_valid(pdata->irq1_gpio)) { + ret = gpio_request(pdata->irq1_gpio, "irq_gpio"); + if (ret) { + pr_err("%s: Failed to request irq_gpio %d\n", + __func__, ret); + goto err_exit; + } + + ret = gpio_direction_input(pdata->irq1_gpio); + if (ret) { + pr_err("%s: Failed gpio_direction irq %d\n", + __func__, ret); + goto err_exit; + } + + state->irq = gpio_to_irq(pdata->irq1_gpio); + if (state->irq) { + ret = request_irq(state->irq, adv7481_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + DRIVER_NAME, state); + if (ret) { + pr_err("%s: Failed request_irq %d\n", + __func__, ret); + goto err_exit; + } + } else { + pr_err("%s: Failed gpio_to_irq %d\n", __func__, ret); + ret = -EINVAL; + goto err_exit; + } + + /* disable irq until chip interrupts are programmed */ + disable_irq(state->irq); + + INIT_DELAYED_WORK(&state->irq_delayed_work, + adv7481_irq_delay_work); + } + +err_exit: + mutex_unlock(&state->mutex); + + return ret; +} + +static int adv7481_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct adv7481_state *state = to_state(sd); + int temp = 0x0; + int ret = 0; + + pr_debug("Enter %s: id = 0x%x\n", __func__, ctrl->id); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp |= CP_CTR_VID_ADJ_EN; + ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(state->client, + CP_REG_BRIGHTNESS, ctrl->val); + break; + case V4L2_CID_CONTRAST: + temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp |= CP_CTR_VID_ADJ_EN; + ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(state->client, + CP_REG_CONTRAST, ctrl->val); + break; + case V4L2_CID_SATURATION: + temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp |= CP_CTR_VID_ADJ_EN; + ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(state->client, + CP_REG_SATURATION, ctrl->val); + break; + case V4L2_CID_HUE: + temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp |= CP_CTR_VID_ADJ_EN; + ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(state->client, CP_REG_HUE, ctrl->val); + break; + default: + break; + } + return ret; +} + +static int adv7481_powerup(struct adv7481_state *state, bool powerup) +{ + int ret = 0; + + if (powerup) { + pr_debug("%s: powered up\n", __func__); + } else { + pr_debug("%s: powered off\n", __func__); + ret = adv7481_cec_wakeup(state, !powerup); + } + return ret; +} + +static int adv7481_s_power(struct v4l2_subdev *sd, int on) +{ + struct adv7481_state *state = to_state(sd); + int ret; + + pr_debug("Enter %s\n", __func__); + ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return -EBUSY; + + ret = adv7481_powerup(state, on); + if (ret == 0) + state->powerup = on; + + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) +{ + int ret = 0; + int sdp_stat, sdp_stat2; + int timeout = 0; + + if (sd_standard == NULL) + return -EINVAL; + + do { + sdp_stat = adv7481_rd_byte(state->i2c_sdp, + SDP_RO_MAIN_STATUS1_ADDR); + usleep_range(SDP_MIN_SLEEP, SDP_MAX_SLEEP); + timeout++; + sdp_stat2 = adv7481_rd_byte(state->i2c_sdp, + SDP_RO_MAIN_STATUS1_ADDR); + } while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES)); + + if (sdp_stat != sdp_stat2) { + pr_err("%s(%d), adv7481 SDP status unstable: 1\n", + __func__, __LINE__); + return -ETIMEDOUT; + } + + if (!ADV_REG_GETFIELD(sdp_stat, SDP_RO_MAIN_IN_LOCK)) { + pr_err("%s(%d), adv7481 SD Input NOT Locked: 0x%x\n", + __func__, __LINE__, sdp_stat); + return -EBUSY; + } + + switch (ADV_REG_GETFIELD(sdp_stat, SDP_RO_MAIN_AD_RESULT)) { + case AD_NTSM_M_J: + *sd_standard = V4L2_STD_NTSC; + break; + case AD_NTSC_4_43: + *sd_standard = V4L2_STD_NTSC_443; + break; + case AD_PAL_M: + *sd_standard = V4L2_STD_PAL_M; + break; + case AD_PAL_60: + *sd_standard = V4L2_STD_PAL_60; + break; + case AD_PAL_B_G: + *sd_standard = V4L2_STD_PAL; + break; + case AD_SECAM: + *sd_standard = V4L2_STD_SECAM; + break; + case AD_PAL_COMB_N: + *sd_standard = V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + break; + case AD_SECAM_525: + *sd_standard = V4L2_STD_SECAM; + break; + default: + *sd_standard = V4L2_STD_UNKNOWN; + break; + } + return ret; +} + +static int adv7481_set_cvbs_mode(struct adv7481_state *state) +{ + int ret; + uint8_t val; + + pr_debug("Enter %s\n", __func__); + state->mode = ADV7481_IP_CVBS_1; + /* cvbs video settings ntsc etc */ + ret = adv7481_wr_byte(state->client, 0x00, 0x30); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x0f, 0x00); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x00, 0x00); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x03, 0x42); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x04, 0x07); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x13, 0x00); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x17, 0x41); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x31, 0x12); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x52, 0xcd); + ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0xff); + val = adv7481_rd_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR); + /* Output of SD core routed to MIPI CSI 4-lane Tx */ + val |= ADV_REG_SETFIELD(0x10, IO_CTRL_CSI4_IN_SEL); + ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, val); + /* Enable autodetect */ + ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0x81); + + return ret; +} + +static int adv7481_set_hdmi_mode(struct adv7481_state *state) +{ + int ret; + int temp; + uint8_t val; + + pr_debug("Enter %s\n", __func__); + state->mode = ADV7481_IP_HDMI; + /* Configure IO setting for HDMI in and + * YUV 422 out via TxA CSI: 4-Lane + */ + /* Disable chip powerdown & Enable HDMI Rx block */ + temp = adv7481_rd_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR); + val = ADV_REG_SETFIELD(1, IO_CTRL_RX_EN) | + ADV_REG_SETFIELD(0, IO_CTRL_RX_PWDN) | + ADV_REG_SETFIELD(0, IO_CTRL_XTAL_PWDN) | + ADV_REG_SETFIELD(0, IO_CTRL_CORE_PWDN) | + ADV_REG_SETFIELD(0, IO_CTRL_MASTER_PWDN); + ret = adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, val); + /* SDR mode */ + ret |= adv7481_wr_byte(state->client, 0x11, 0x48); + /* Set CP core to YUV out */ + ret |= adv7481_wr_byte(state->client, 0x04, 0x00); + /* Set CP core to SDR 422 */ + ret |= adv7481_wr_byte(state->client, 0x12, 0xF2); + /* Saturate both Luma and Chroma values to 254 */ + ret |= adv7481_wr_byte(state->client, 0x17, 0x80); + /* Set CP core to enable AV codes */ + ret |= adv7481_wr_byte(state->client, 0x03, 0x86); + /* ADI RS CP Core: */ + ret |= adv7481_wr_byte(state->i2c_cp, 0x7C, 0x00); + /* Set CP core Phase Adjustment */ + ret |= adv7481_wr_byte(state->client, 0x0C, 0xE0); + /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ + ret |= adv7481_wr_byte(state->client, IO_PAD_CTRLS_ADDR, 0xDD); + /* Enable Tx A CSI 4-Lane & data from CP core */ + val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); + ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, + val); + + /* start to configure HDMI Rx once io-map is configured */ + /* Enable HDCP 1.1 */ + ret |= adv7481_wr_byte(state->i2c_rep, 0x40, 0x83); + /* Foreground Channel = A */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x00, 0x08); + /* ADI Required Write */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x98, 0xFF); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x99, 0xA3); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9A, 0x00); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9B, 0x0A); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9D, 0x40); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0xCB, 0x09); + /* ADI RS */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3D, 0x10); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3E, 0x7B); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3F, 0x5E); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4E, 0xFE); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4F, 0x18); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x57, 0xA3); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x58, 0x04); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x85, 0x10); + /* Enable All Terminations */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x83, 0x00); + /* ADI RS */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0xA3, 0x01); + ret |= adv7481_wr_byte(state->i2c_hdmi, 0xBE, 0x00); + /* HPA Manual Enable */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x6C, 0x01); + /* HPA Asserted */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0xF8, 0x01); + + /* Audio Mute Speed Set to Fastest (Smallest Step Size) */ + ret |= adv7481_wr_byte(state->i2c_hdmi, 0x0F, 0x00); + + return ret; +} + +static int adv7481_set_analog_mux(struct adv7481_state *state, int input) +{ + int ain_sel = 0x0; + + switch (input) { + case ADV7481_IP_CVBS_1: + case ADV7481_IP_CVBS_1_HDMI_SIM: + ain_sel = 0x0; + break; + case ADV7481_IP_CVBS_2: + case ADV7481_IP_CVBS_2_HDMI_SIM: + ain_sel = 0x1; + break; + case ADV7481_IP_CVBS_3: + case ADV7481_IP_CVBS_3_HDMI_SIM: + ain_sel = 0x2; + break; + case ADV7481_IP_CVBS_4: + case ADV7481_IP_CVBS_4_HDMI_SIM: + ain_sel = 0x3; + break; + case ADV7481_IP_CVBS_5: + case ADV7481_IP_CVBS_5_HDMI_SIM: + ain_sel = 0x4; + break; + case ADV7481_IP_CVBS_6: + case ADV7481_IP_CVBS_6_HDMI_SIM: + ain_sel = 0x5; + break; + case ADV7481_IP_CVBS_7: + case ADV7481_IP_CVBS_7_HDMI_SIM: + ain_sel = 0x6; + break; + case ADV7481_IP_CVBS_8: + case ADV7481_IP_CVBS_8_HDMI_SIM: + ain_sel = 0x7; + break; + } + return 0; +} + +static int adv7481_set_ip_mode(struct adv7481_state *state, int input) +{ + int ret = 0; + + pr_debug("Enter %s: input: %d\n", __func__, input); + switch (input) { + case ADV7481_IP_HDMI: + ret = adv7481_set_hdmi_mode(state); + break; + case ADV7481_IP_CVBS_1: + case ADV7481_IP_CVBS_2: + case ADV7481_IP_CVBS_3: + case ADV7481_IP_CVBS_4: + case ADV7481_IP_CVBS_5: + case ADV7481_IP_CVBS_6: + case ADV7481_IP_CVBS_7: + case ADV7481_IP_CVBS_8: + ret = adv7481_set_cvbs_mode(state); + ret |= adv7481_set_analog_mux(state, input); + break; + case ADV7481_IP_CVBS_1_HDMI_SIM: + case ADV7481_IP_CVBS_2_HDMI_SIM: + case ADV7481_IP_CVBS_3_HDMI_SIM: + case ADV7481_IP_CVBS_4_HDMI_SIM: + case ADV7481_IP_CVBS_5_HDMI_SIM: + case ADV7481_IP_CVBS_6_HDMI_SIM: + case ADV7481_IP_CVBS_7_HDMI_SIM: + case ADV7481_IP_CVBS_8_HDMI_SIM: + ret = adv7481_set_hdmi_mode(state); + ret |= adv7481_set_cvbs_mode(state); + ret |= adv7481_set_analog_mux(state, input); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int adv7481_set_op_src(struct adv7481_state *state, + int output, int input) +{ + int ret = 0; + int temp = 0; + int val = 0; + + pr_debug("Enter %s: output: %d, input: %d\n", __func__, output, input); + switch (output) { + case ADV7481_OP_CSIA: + switch (input) { + case ADV7481_IP_CVBS_1: + case ADV7481_IP_CVBS_2: + case ADV7481_IP_CVBS_3: + case ADV7481_IP_CVBS_4: + case ADV7481_IP_CVBS_5: + case ADV7481_IP_CVBS_6: + case ADV7481_IP_CVBS_7: + case ADV7481_IP_CVBS_8: + val = 0x10; + break; + case ADV7481_IP_CVBS_1_HDMI_SIM: + case ADV7481_IP_CVBS_2_HDMI_SIM: + case ADV7481_IP_CVBS_3_HDMI_SIM: + case ADV7481_IP_CVBS_4_HDMI_SIM: + case ADV7481_IP_CVBS_5_HDMI_SIM: + case ADV7481_IP_CVBS_6_HDMI_SIM: + case ADV7481_IP_CVBS_7_HDMI_SIM: + case ADV7481_IP_CVBS_8_HDMI_SIM: + case ADV7481_IP_HDMI: + val = 0x00; + break; + case ADV7481_IP_TTL: + val = 0x01; + break; + default: + ret = -EINVAL; + } + temp = adv7481_rd_byte(state->client, + IO_REG_PWR_DOWN_CTRL_ADDR); + temp |= val; + adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, temp); + state->csia_src = input; + break; + case ADV7481_OP_CSIB: + if (input != ADV7481_IP_HDMI && input != ADV7481_IP_TTL) + state->csib_src = input; + else + ret = -EINVAL; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static u32 ba_inp_to_adv7481(u32 input) +{ + u32 adv_input = ADV7481_IP_HDMI; + + switch (input) { + case BA_IP_CVBS_0: + adv_input = ADV7481_IP_CVBS_1; + break; + case BA_IP_CVBS_1: + adv_input = ADV7481_IP_CVBS_2; + break; + case BA_IP_CVBS_2: + adv_input = ADV7481_IP_CVBS_3; + break; + case BA_IP_CVBS_3: + adv_input = ADV7481_IP_CVBS_4; + break; + case BA_IP_CVBS_4: + adv_input = ADV7481_IP_CVBS_5; + break; + case BA_IP_CVBS_5: + adv_input = ADV7481_IP_CVBS_6; + break; + case BA_IP_HDMI_1: + adv_input = ADV7481_IP_HDMI; + break; + case BA_IP_MHL_1: + adv_input = ADV7481_IP_HDMI; + break; + case BA_IP_TTL: + adv_input = ADV7481_IP_TTL; + break; + default: + adv_input = ADV7481_IP_HDMI; + break; + } + return adv_input; +} + +static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + int adv_input = ba_inp_to_adv7481(input); + struct adv7481_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + + if (ret) + return ret; + + pr_debug("Enter %s\n", __func__); + ret = adv7481_set_op_src(state, output, adv_input); + if (ret) { + pr_err("%s: Output SRC Routing Error: %d\n", __func__, ret); + goto unlock_exit; + } + + if (state->mode != adv_input) { + ret = adv7481_set_ip_mode(state, adv_input); + if (ret) + pr_err("%s: Set input mode failed: %d\n", + __func__, ret); + else + state->mode = adv_input; + } + +unlock_exit: + mutex_unlock(&state->mutex); + + return ret; +} + +static int adv7481_get_hdmi_timings(struct adv7481_state *state, + struct adv7481_vid_params *vid_params, + struct adv7481_hdmi_params *hdmi_params) +{ + int ret = 0; + int temp1 = 0; + int temp2 = 0; + int fieldfactor = 0; + uint32_t count = 0; + + pr_debug("Enter %s\n", __func__); + /* Check TMDS PLL Lock and Frequency */ + temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM4_ADDR); + hdmi_params->pll_lock = ADV_REG_GETFIELD(temp1, + HDMI_REG_TMDS_PLL_LOCKED); + if (hdmi_params->pll_lock) { + temp1 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_TMDS_FREQ_ADDR); + temp2 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_TMDS_FREQ_FRAC_ADDR); + hdmi_params->tmds_freq = ADV_REG_GETFIELD(temp1, + HDMI_REG_TMDS_FREQ); + hdmi_params->tmds_freq = (hdmi_params->tmds_freq << 1) + + ADV_REG_GETFIELD(temp2, + HDMI_REG_TMDS_FREQ_0); + hdmi_params->tmds_freq *= ONE_MHZ_TO_HZ; + hdmi_params->tmds_freq += ADV_REG_GETFIELD(temp2, + HDMI_REG_TMDS_FREQ_FRAC)*ONE_MHZ_TO_HZ/128; + } else { + pr_err("%s: PLL not locked return EBUSY\n", __func__); + return -EBUSY; + } + + /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + do { + temp1 = adv7481_rd_byte(state->client, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); + temp2 = adv7481_rd_byte(state->i2c_cp, + CP_REG_STDI_CH_ADDR); + + if (ADV_REG_GETFIELD(temp1, IO_DE_REGEN_LCK_RAW) && + ADV_REG_GETFIELD(temp1, IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(temp1, IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(temp2, CP_STDI_DVALID_CH1)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + if (count >= LOCK_NUM_TRIES) { + pr_err("%s(%d), adv7481 HDMI DE regeneration block NOT Locked: 0x%x", + __func__, __LINE__, temp1); + } + + /* Check Timing Lock HDMI Map V:0x07[7], H:0x7[5] */ + do { + temp1 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_LINE_WIDTH_1_ADDR); + + if (ADV_REG_GETFIELD(temp1, HDMI_VERT_FILTER_LOCKED) && + ADV_REG_GETFIELD(temp1, HDMI_DE_REGEN_FILTER_LCK)) { + break; + } + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + if (count >= LOCK_NUM_TRIES) { + pr_err("%s(%d), adv7481 HDMI DE filter NOT Locked: 0x%x\n", + __func__, __LINE__, temp1); + } + + /* Check HDMI Parameters */ + temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_FIELD1_HEIGHT1_ADDR); + hdmi_params->color_depth = ADV_REG_GETFIELD(temp1, + HDMI_REG_DEEP_COLOR_MODE); + + /* Check Interlaced and Field Factor */ + vid_params->intrlcd = ADV_REG_GETFIELD(temp1, + HDMI_REG_HDMI_INTERLACED); + fieldfactor = (vid_params->intrlcd == 1) ? 2 : 1; + + temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM5_ADDR); + hdmi_params->pix_rep = ADV_REG_GETFIELD(temp1, + HDMI_REG_PIXEL_REPETITION); + + /* Get Active Timing Data HDMI Map H:0x07[4:0] + 0x08[7:0] */ + temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_1_ADDR); + temp2 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_2_ADDR); + vid_params->act_pix = (((ADV_REG_GETFIELD(temp1, + HDMI_REG_LINE_WIDTH_1) << 8) & 0x1F00) | + ADV_REG_GETFIELD(temp2, + HDMI_REG_LINE_WIDTH_2)); + + /* Get Total Timing Data HDMI Map H:0x1E[5:0] + 0x1F[7:0] */ + temp1 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR); + temp2 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR); + vid_params->tot_pix = (((ADV_REG_GETFIELD(temp1, + HDMI_REG_TOTAL_LINE_WIDTH_1) << 8) & 0x3F00) | + ADV_REG_GETFIELD(temp2, + HDMI_REG_TOTAL_LINE_WIDTH_2)); + + /* Get Active Timing Data HDMI Map V:0x09[4:0] + 0x0A[7:0] */ + temp1 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_FIELD0_HEIGHT_1_ADDR); + temp2 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_FIELD0_HEIGHT_2_ADDR); + vid_params->act_lines = (((ADV_REG_GETFIELD(temp1, + HDMI_REG_FIELD0_HEIGHT_1) << 8) & 0x1F00) | + ADV_REG_GETFIELD(temp2, + HDMI_REG_FIELD0_HEIGHT_2)); + + /* Get Total Timing Data HDMI Map V:0x26[5:0] + 0x27[7:0] */ + temp1 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR); + temp2 = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR); + vid_params->tot_lines = (((ADV_REG_GETFIELD(temp1, + HDMI_REG_FIELD0_TOT_HEIGHT_1) << 8) & 0x3F00) | + ADV_REG_GETFIELD(temp2, + HDMI_REG_FIELD0_TOT_HEIGHT_2)); + vid_params->tot_lines /= 2; + + vid_params->pix_clk = hdmi_params->tmds_freq; + + switch (hdmi_params->color_depth) { + case CD_10BIT: + vid_params->pix_clk = ((vid_params->pix_clk*4)/5); + break; + case CD_12BIT: + vid_params->pix_clk = ((vid_params->pix_clk*2)/3); + break; + case CD_16BIT: + vid_params->pix_clk = (vid_params->pix_clk/2); + break; + case CD_8BIT: + default: + vid_params->pix_clk /= 1; + break; + } + + if ((vid_params->tot_pix != 0) && (vid_params->tot_lines != 0)) { + vid_params->fr_rate = + DIV_ROUND_CLOSEST(vid_params->pix_clk * fieldfactor, + vid_params->tot_lines); + vid_params->fr_rate = DIV_ROUND_CLOSEST(vid_params->fr_rate, + vid_params->tot_pix); + vid_params->fr_rate = DIV_ROUND_CLOSEST(vid_params->fr_rate, + (hdmi_params->pix_rep + 1)); + } + + pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d @ %d fps\n", + __func__, __LINE__, + vid_params->act_pix, vid_params->act_lines, + vid_params->fr_rate); + return ret; +} + +static int adv7481_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int ret; + struct adv7481_state *state = to_state(sd); + struct adv7481_vid_params vid_params; + struct adv7481_hdmi_params hdmi_params; + struct v4l2_bt_timings *bt_timings = &timings->bt; + + if (!timings) + return -EINVAL; + + pr_debug("Enter %s\n", __func__); + ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + memset(&vid_params, 0, sizeof(struct adv7481_vid_params)); + memset(&hdmi_params, 0, sizeof(struct adv7481_hdmi_params)); + + switch (state->mode) { + case ADV7481_IP_HDMI: + case ADV7481_IP_CVBS_1_HDMI_SIM: + adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); + timings->type = V4L2_DV_BT_656_1120; + bt_timings->width = vid_params.act_pix; + bt_timings->height = vid_params.act_lines; + bt_timings->pixelclock = vid_params.pix_clk; + bt_timings->interlaced = vid_params.intrlcd ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + if (bt_timings->interlaced == V4L2_DV_INTERLACED) + bt_timings->height /= 2; + break; + default: + return -EINVAL; + } + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + int ret = 0; + int temp = 0; + struct adv7481_state *state = to_state(sd); + uint8_t tStatus = 0x0; + + pr_debug("Enter %s\n", __func__); + tStatus = adv7481_rd_byte(state->i2c_sdp, SDP_RO_MAIN_STATUS1_ADDR); + if (!ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + pr_err("%s(%d), adv7481 SD Input NOT Locked: 0x%x\n", + __func__, __LINE__, tStatus); + + if (!std) + return -EINVAL; + + switch (state->mode) { + case ADV7481_IP_CVBS_1: + case ADV7481_IP_CVBS_2: + case ADV7481_IP_CVBS_3: + case ADV7481_IP_CVBS_4: + case ADV7481_IP_CVBS_5: + case ADV7481_IP_CVBS_6: + case ADV7481_IP_CVBS_7: + case ADV7481_IP_CVBS_8: + case ADV7481_IP_CVBS_1_HDMI_SIM: + case ADV7481_IP_CVBS_2_HDMI_SIM: + case ADV7481_IP_CVBS_3_HDMI_SIM: + case ADV7481_IP_CVBS_4_HDMI_SIM: + case ADV7481_IP_CVBS_5_HDMI_SIM: + case ADV7481_IP_CVBS_6_HDMI_SIM: + case ADV7481_IP_CVBS_7_HDMI_SIM: + case ADV7481_IP_CVBS_8_HDMI_SIM: + ret = adv7481_get_sd_timings(state, &temp); + break; + default: + return -EINVAL; + } + + if (!tStatus) + *std = (v4l2_std_id) temp; + else + *std = V4L2_STD_UNKNOWN; + + return ret; +} + +static int adv7481_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + pr_debug("Enter %s\n", __func__); + + interval->interval.numerator = 1; + interval->interval.denominator = 60; + + return 0; +} + +static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int ret; + struct adv7481_vid_params vid_params; + struct adv7481_hdmi_params hdmi_params; + struct adv7481_state *state = to_state(sd); + + if (!fmt) + return -EINVAL; + + pr_debug("Enter %s\n", __func__); + ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + memset(&vid_params, 0, sizeof(struct adv7481_vid_params)); + memset(&hdmi_params, 0, sizeof(struct adv7481_hdmi_params)); + + switch (state->mode) { + case ADV7481_IP_HDMI: + case ADV7481_IP_CVBS_1_HDMI_SIM: + adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); + fmt->width = vid_params.act_pix; + fmt->height = vid_params.act_lines; + if (vid_params.intrlcd) + fmt->height /= 2; + break; + default: + return -EINVAL; + } + mutex_unlock(&state->mutex); + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + return ret; +} + +static int adv7481_set_audio_spdif(struct adv7481_state *state, + bool on) +{ + int ret; + uint8_t val; + + if (on) { + /* Configure I2S_SDATA output pin as an SPDIF output 0x6E[3] */ + val = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); + val |= ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); + ret = adv7481_wr_byte(state->i2c_hdmi, + HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); + } else { + /* Configure I2S_SDATA output pin as an I2S output 0x6E[3] */ + val = adv7481_rd_byte(state->i2c_hdmi, + HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); + val &= ~ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); + ret = adv7481_wr_byte(state->i2c_hdmi, + HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); + } + return ret; +} + +static int adv7481_csi_powerdown(struct adv7481_state *state, + enum adv7481_output output) +{ + int ret; + struct i2c_client *csi_map; + uint8_t val = 0; + + pr_debug("Enter %s for output: %d\n", __func__, output); + /* Select CSI TX to configure data */ + if (output == ADV7481_OP_CSIA) { + csi_map = state->i2c_csi_txa; + } else if (output == ADV7481_OP_CSIB) { + csi_map = state->i2c_csi_txb; + } else if (output == ADV7481_OP_TTL) { + /* For now use TxA */ + csi_map = state->i2c_csi_txa; + } else { + /* Default to TxA */ + csi_map = state->i2c_csi_txa; + } + /* CSI Tx: power down DPHY */ + ret = adv7481_wr_byte(csi_map, CSI_REG_TX_DPHY_PWDN_ADDR, + ADV_REG_SETFIELD(1, CSI_CTRL_DPHY_PWDN)); + /* ADI Required Write */ + ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(csi_map, 0x1e, 0x00); + /* CSI TxA: # Lane : Power Off */ + val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | + ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); + ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + /* + * ADI Recommended power down sequence + * DPHY and CSI Tx A Power down Sequence + * CSI TxA: MIPI PLL DIS + */ + ret |= adv7481_wr_byte(csi_map, 0xda, 0x00); + /* ADI Required Write */ + ret |= adv7481_wr_byte(csi_map, 0xc1, 0x3b); + + pr_debug("Exit %s, ret: %d\n", __func__, ret); + + return ret; +} + +static int adv7481_csi_powerup(struct adv7481_state *state, + enum adv7481_output output) +{ + int ret; + struct i2c_client *csi_map; + uint8_t val = 0; + uint8_t csi_sel = 0; + + pr_debug("Enter %s for output: %d\n", __func__, output); + /* Select CSI TX to configure data */ + if (output == ADV7481_OP_CSIA) { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); + csi_map = state->i2c_csi_txa; + } else if (output == ADV7481_OP_CSIB) { + /* Enable 1-Lane MIPI Tx, enable pixel output and + * route SD through Pixel port + */ + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI1_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_SD_THRU_PIX_OUT) | + ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); + csi_map = state->i2c_csi_txb; + } else if (output == ADV7481_OP_TTL) { + /* For now use TxA */ + csi_map = state->i2c_csi_txa; + } else { + /* Default to TxA */ + csi_map = state->i2c_csi_txa; + } + + /* Enable Tx A/B CSI #-lane */ + ret = adv7481_wr_byte(state->client, + IO_REG_CSI_PIX_EN_SEL_ADDR, csi_sel); + /* TXA MIPI lane settings for CSI */ + /* CSI TxA: # Lane : Power Off */ + val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | + ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); + ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + /* CSI TxA: Auto D-PHY Timing */ + val |= ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS); + ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + + /* DPHY and CSI Tx A */ + ret |= adv7481_wr_byte(csi_map, 0xdb, 0x10); + ret |= adv7481_wr_byte(csi_map, 0xd6, 0x07); + ret |= adv7481_wr_byte(csi_map, 0xc4, 0x0a); + ret |= adv7481_wr_byte(csi_map, 0x71, 0x33); + ret |= adv7481_wr_byte(csi_map, 0x72, 0x11); + /* CSI TxA: power up DPHY */ + ret |= adv7481_wr_byte(csi_map, 0xf0, 0x00); + /* ADI Required Write */ + ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(csi_map, 0x1e, 0x40); + /* adi Recommended power up sequence */ + /* DPHY and CSI Tx A Power up Sequence */ + /* CSI TxA: MIPI PLL EN */ + ret |= adv7481_wr_byte(csi_map, 0xda, 0x01); + msleep(200); + /* CSI TxA: # MIPI Lane : Power ON */ + val = ADV_REG_SETFIELD(0, CSI_CTRL_TX_PWRDN) | + ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS) | + ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); + ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + msleep(100); + /* ADI Required Write */ + ret |= adv7481_wr_byte(csi_map, 0xc1, 0x2b); + msleep(100); + /* ADI Required Write */ + ret |= adv7481_wr_byte(csi_map, 0x31, 0x80); + + pr_debug("Exit %s, ret: %d\n", __func__, ret); + + return ret; +} + +static int adv7481_set_op_stream(struct adv7481_state *state, bool on) +{ + int ret = 0; + + pr_debug("Enter %s: on: %d, a src: %d, b src: %d\n", + __func__, on, state->csia_src, state->csib_src); + if (on && state->csia_src != ADV7481_IP_NONE) + if (ADV7481_IP_HDMI == state->csia_src) { + state->tx_lanes = ADV7481_MIPI_2LANE; + ret = adv7481_set_audio_spdif(state, on); + ret |= adv7481_csi_powerup(state, ADV7481_OP_CSIA); + } else { + state->tx_lanes = ADV7481_MIPI_1LANE; + ret = adv7481_csi_powerup(state, ADV7481_OP_CSIA); + } + else if (on && state->csib_src != ADV7481_IP_NONE) { + /* CSI Tx B is always 1 lane */ + state->tx_lanes = ADV7481_MIPI_1LANE; + ret = adv7481_csi_powerup(state, ADV7481_OP_CSIB); + } else { + /* Turn off */ + if (ADV7481_IP_NONE != state->csia_src) { + if (ADV7481_IP_HDMI == state->csia_src) { + state->tx_lanes = ADV7481_MIPI_1LANE; + ret = adv7481_set_audio_spdif(state, on); + } else { + state->tx_lanes = ADV7481_MIPI_1LANE; + } + ret |= adv7481_csi_powerdown(state, ADV7481_OP_CSIA); + } else if (ADV7481_IP_NONE != state->csib_src) { + /* CSI Tx B is always 1 lane */ + state->tx_lanes = ADV7481_MIPI_1LANE; + ret = adv7481_csi_powerdown(state, ADV7481_OP_CSIB); + } else { + /* CSI TxA and all 4 lanes off */ + state->tx_lanes = ADV7481_MIPI_4LANE; + ret = adv7481_csi_powerdown(state, ADV7481_OP_CSIA); + /* CSI Tx B is always 1 lane */ + state->tx_lanes = ADV7481_MIPI_1LANE; + ret |= adv7481_csi_powerdown(state, ADV7481_OP_CSIB); + } + } + return ret; +} + +static int adv7481_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + int ret = 0; + struct adv7481_state *state = to_state(sd); + uint8_t val1 = 0; + uint8_t val2 = 0; + uint32_t count = 0; + + pr_debug("Enter %s\n", __func__); + if (ADV7481_IP_HDMI == state->mode) { + /* + * Check Timing Lock IO Map Status3:0x71[0] && + * 0x71[1] && 0x71[7] + */ + do { + val1 = adv7481_rd_byte(state->client, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); + val2 = adv7481_rd_byte(state->i2c_cp, + CP_REG_STDI_CH_ADDR); + + if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && + ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + if (count >= LOCK_NUM_TRIES) { + pr_err("%s(%d), HDMI DE regeneration block NOT Locked: 0x%x, 0x%x", + __func__, __LINE__, val1, val2); + *status |= V4L2_IN_ST_NO_SIGNAL; + } + } else { + val1 = adv7481_rd_byte(state->i2c_sdp, + SDP_RO_MAIN_STATUS1_ADDR); + if (!ADV_REG_GETFIELD(val1, SDP_RO_MAIN_IN_LOCK)) { + pr_err("%s(%d), SD Input NOT Locked: 0x%x\n", + __func__, __LINE__, val1); + *status |= V4L2_IN_ST_NO_SIGNAL; + } + } + return ret; +} + +static int adv7481_s_stream(struct v4l2_subdev *sd, int on) +{ + struct adv7481_state *state = to_state(sd); + int ret; + + ret = adv7481_set_op_stream(state, on); + return ret; +} + +static const struct v4l2_subdev_video_ops adv7481_video_ops = { + .s_routing = adv7481_s_routing, + .g_frame_interval = adv7481_g_frame_interval, + .g_mbus_fmt = adv7481_g_mbus_fmt, + .querystd = adv7481_query_sd_std, + .g_dv_timings = adv7481_query_dv_timings, + .g_input_status = adv7481_g_input_status, + .s_stream = adv7481_s_stream, +}; + +static const struct v4l2_subdev_core_ops adv7481_core_ops = { + .s_power = adv7481_s_power, +}; + +static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { + .s_ctrl = adv7481_s_ctrl, +}; + +static const struct v4l2_subdev_ops adv7481_ops = { + .core = &adv7481_core_ops, + .video = &adv7481_video_ops, +}; + +static int adv7481_init_v4l2_controls(struct adv7481_state *state) +{ + v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); + + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, + V4L2_CID_HUE, -127, 128, 1, 0); + + state->sd.ctrl_handler = &state->ctrl_hdl; + if (state->ctrl_hdl.error) { + int err = state->ctrl_hdl.error; + + v4l2_ctrl_handler_free(&state->ctrl_hdl); + return err; + } + v4l2_ctrl_handler_setup(&state->ctrl_hdl); + + return 0; +} + +static int adv7481_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7481_state *state; + struct adv7481_platform_data *pdata = NULL; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *hdl; + int ret; + + pr_debug("Attempting to probe...\n"); + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + pr_err("%s %s Check i2c Functionality Fail\n", + __func__, client->name); + ret = -EIO; + goto err; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr, client->adapter->name); + + /* Create 7481 State */ + state = devm_kzalloc(&client->dev, + sizeof(struct adv7481_state), GFP_KERNEL); + if (state == NULL) { + ret = -ENOMEM; + pr_err("Check Kzalloc Fail\n"); + goto err_mem; + } + state->client = client; + mutex_init(&state->mutex); + + /* Get and Check Platform Data */ + pdata = (struct adv7481_platform_data *) client->dev.platform_data; + if (!pdata) { + ret = -ENOMEM; + pr_err("Getting Platform data failed\n"); + goto err_mem; + } + + /* Configure and Register V4L2 I2C Sub-device */ + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7481_ops); + state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + state->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + + /* Register as Media Entity */ + state->pad.flags = MEDIA_PAD_FL_SOURCE; + state->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV; + ret = media_entity_init(&state->sd.entity, 1, &state->pad, 0); + if (ret) { + ret = -EIO; + pr_err("Media entity init failed\n"); + goto err_media_entity; + } + + /* Initialize HW Config */ + ret = adv7481_hw_init(pdata, state); + if (ret) { + ret = -EIO; + pr_err("HW Initialisation Failed\n"); + goto err_media_entity; + } + + /* Register V4l2 Control Functions */ + hdl = &state->ctrl_hdl; + v4l2_ctrl_handler_init(hdl, 4); + adv7481_init_v4l2_controls(state); + + /* Initials ADV7481 State Settings */ + state->tx_auto_params = ADV7481_AUTO_PARAMS; + state->tx_lanes = ADV7481_MIPI_2LANE; + + /* Initialize SW Init Settings and I2C sub maps 7481 */ + ret = adv7481_dev_init(state, client); + if (ret) { + ret = -EIO; + pr_err("SW Initialisation Failed\n"); + goto err_media_entity; + } + + /* Set hdmi settings */ + ret = adv7481_set_hdmi_mode(state); + + /* BA registration */ + ret |= msm_ba_register_subdev_node(sd); + if (ret) { + ret = -EIO; + pr_err("BA INIT FAILED\n"); + goto err_media_entity; + } + pr_debug("Probe successful!\n"); + + return ret; + +err_media_entity: + media_entity_cleanup(&sd->entity); +err_mem: + kfree(state); +err: + if (!ret) + ret = 1; + return ret; +} + +static int adv7481_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7481_state *state = to_state(sd); + + msm_ba_unregister_subdev_node(sd); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + + v4l2_ctrl_handler_free(&state->ctrl_hdl); + + if (state->irq > 0) + free_irq(state->irq, state); + + i2c_unregister_device(state->i2c_csi_txa); + i2c_unregister_device(state->i2c_csi_txb); + i2c_unregister_device(state->i2c_hdmi); + i2c_unregister_device(state->i2c_edid); + i2c_unregister_device(state->i2c_cp); + i2c_unregister_device(state->i2c_sdp); + i2c_unregister_device(state->i2c_rep); + mutex_destroy(&state->mutex); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7481_id[] = { + { DRIVER_NAME, 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7481_id); + + +static struct i2c_driver adv7481_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + }, + .probe = adv7481_probe, + .remove = adv7481_remove, + .id_table = adv7481_id, +}; + +module_i2c_driver(adv7481_driver); + +MODULE_DESCRIPTION("ADI ADV7481 HDMI/MHL/SD video receiver"); diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h new file mode 100644 index 000000000000..a4e14fa18e0a --- /dev/null +++ b/drivers/media/i2c/adv7481_reg.h @@ -0,0 +1,347 @@ +/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ +#ifndef __ADV7481_REG_H__ +#define __ADV7481_REG_H__ + +#define ADV_REG_SETFIELD(val, field) \ + (((val) << (field##_SHFT)) & (field##_BMSK)) + +#define ADV_REG_GETFIELD(val, field) \ + (((val) & (field##_BMSK)) >> (field##_SHFT)) + +/* IO Map Registers */ +#define IO_REG_MAIN_RST_ADDR 0xFF +#define IO_REG_MAIN_RST_VALUE 0xFF + +#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 +#define IO_CTRL_RX_EN_BMSK 0x0040 +#define IO_CTRL_RX_EN_SHFT 6 +#define IO_CTRL_RX_PWDN_BMSK 0x0020 +#define IO_CTRL_RX_PWDN_SHFT 5 +#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 +#define IO_CTRL_XTAL_PWDN_SHFT 2 +#define IO_CTRL_CORE_PWDN_BMSK 0x0002 +#define IO_CTRL_CORE_PWDN_SHFT 1 +#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 +#define IO_CTRL_MASTER_PWDN_SHFT 0 + +#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 +#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F +#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 + +#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 +#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF +#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 + +#define IO_REG_CP_VID_STD_ADDR 0x05 + +#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 +#define IO_CTRL_CSI4_EN_BMSK 0x0080 +#define IO_CTRL_CSI4_EN_SHFT 7 +#define IO_CTRL_CSI1_EN_BMSK 0x0040 +#define IO_CTRL_CSI1_EN_SHFT 6 +#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 +#define IO_CTRL_PIX_OUT_EN_SHFT 5 +#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 +#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 +#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C +#define IO_CTRL_CSI4_IN_SEL_SHFT 2 + +#define IO_PAD_CTRLS_ADDR 0x0E + +#define IO_REG_I2C_CFG_ADDR 0xF2 +#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 + +#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 + +#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 +#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 +#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 +#define IO_CABLE_DET_A_RAW_BMSK 0x0040 +#define IO_CABLE_DET_A_RAW_SHFT 6 +#define IO_V_LOCKED_RAW_BMSK 0x0002 +#define IO_V_LOCKED_RAW_SHFT 1 +#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 +#define IO_DE_REGEN_LCK_RAW_SHFT 0 + +/* Interrupts */ +#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 +#define IO_CABLE_DET_A_ST_BMSK 0x0040 +#define IO_CABLE_DET_A_ST_SHFT 6 + +#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 +#define IO_CABLE_DET_A_CLR_BMSK 0x0040 +#define IO_CABLE_DET_A_CLR_SHFT 6 + +#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 +#define IO_CABLE_DET_A_MB2_BMSK 0x0040 +#define IO_CABLE_DET_A_MB2_SHFT 6 + +#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 +#define IO_CABLE_DET_A_MB1_BMSK 0x0040 +#define IO_CABLE_DET_A_MB1_SHFT 6 + +#define IO_REG_PAD_CTRL_1_ADDR 0x1D +#define IO_PDN_INT1_BMSK 0x0080 +#define IO_PDN_INT1_SHFT 7 +#define IO_PDN_INT2_BMSK 0x0040 +#define IO_PDN_INT2_SHFT 6 +#define IO_PDN_INT3_BMSK 0x0020 +#define IO_PDN_INT3_SHFT 5 +#define IO_INV_LLC_BMSK 0x0010 +#define IO_INV_LLC_SHFT 4 +#define IO_DRV_LLC_PAD_BMSK 0x000C +#define IO_DRV_LLC_PAD_SHFT 2 + +#define IO_REG_INT_RAW_STATUS_ADDR 0x3F + +#define IO_REG_INT1_CONF_ADDR 0x40 +#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ_DUR_SEL_SHFT 6 +#define IO_INTRQ_OP_SEL_BMSK 0x0003 +#define IO_INTRQ_OP_SEL_SHFT 0 + +#define IO_REG_INT2_CONF_ADDR 0x41 +#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ2_DUR_SEL_SHFT 6 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 +#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 +#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 +#define IO_INT2_EN_BMSK 0x0004 +#define IO_INT2_EN_SHFT 2 +#define IO_INTRQ2_OP_SEL_BMSK 0x0003 +#define IO_INTRQ2_OP_SEL_SHFT 0 + +#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 +#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 +#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 + +#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 +#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 +#define IO_CP_LOCK_CP_MB1_SHFT 7 +#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_MB1_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 +#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 +#define IO_INT_SD_MB1_BMSK 0x0001 +#define IO_INT_SD_MB1_SHFT 0 + +/* Offsets */ +#define IO_REG_DPLL_ADDR 0xF3 +#define IO_REG_CP_ADDR 0xF4 +#define IO_REG_HDMI_ADDR 0xF5 +#define IO_REG_EDID_ADDR 0xF6 +#define IO_REG_HDMI_REP_ADDR 0xF7 +#define IO_REG_HDMI_INF_ADDR 0xF8 +#define IO_REG_CBUS_ADDR 0xF9 +#define IO_REG_CEC_ADDR 0xFA +#define IO_REG_SDP_ADDR 0xFB +#define IO_REG_CSI_TXB_ADDR 0xFC +#define IO_REG_CSI_TXA_ADDR 0xFD + +/* Sub Address Map Locations */ +#define IO_REG_DPLL_SADDR 0x4C +#define IO_REG_CP_SADDR 0x44 +#define IO_REG_HDMI_SADDR 0x74 +#define IO_REG_EDID_SADDR 0x78 +#define IO_REG_HDMI_REP_SADDR 0x64 +#define IO_REG_HDMI_INF_SADDR 0x62 +#define IO_REG_CBUS_SADDR 0xF0 +#define IO_REG_CEC_SADDR 0x82 +#define IO_REG_SDP_SADDR 0xF2 +#define IO_REG_CSI_TXB_SADDR 0x90 +#define IO_REG_CSI_TXA_SADDR 0x94 + +/* HDMI Map Registers */ +#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 +#define HDMI_REG_AV_MUTE_BMSK 0x0040 +#define HDMI_REG_AV_MUTE_SHFT 6 +#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 +#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 +#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 +#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 + +#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 +#define HDMI_REG_HDMI_MODE_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 +#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 +#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 +#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 +#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 +#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 +#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F +#define HDMI_REG_PIXEL_REPETITION_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 +#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 +#define HDMI_VERT_FILTER_LOCKED_SHFT 7 +#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 +#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 +#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 +#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 +#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F +#define HDMI_REG_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 +#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 +#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F +#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 +#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A +#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 + +#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B +#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 +#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 +#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 +#define HDMI_REG_HDMI_INTERLACED_SHFT 5 + +#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E +#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F +#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F +#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 + +#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 +#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 +#define HDMI_DIS_CABLE_DET_RST_SHFT 6 + +#define HDMI_REG_TMDS_FREQ_ADDR 0x51 +#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF +#define HDMI_REG_TMDS_FREQ_SHFT 0 + +#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 +#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F +#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 + +#define HDMI_REG_RST_CTRLS_ADDR 0x5A +#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 +#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 + +#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E +#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 +#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 + +/* HDMI Repeater Map Registers */ +#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 +#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 + +#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 +#define HDMI_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_EDID_A_ENABLE_SHFT 0 + +/* CP Map Registers */ +#define CP_REG_CONTRAST 0x3A +#define CP_REG_SATURATION 0x3B +#define CP_REG_BRIGHTNESS 0x3C +#define CP_REG_HUE 0x3D +#define CP_REG_VID_ADJ 0x3E +#define CP_CTR_VID_ADJ_EN 0x80 +#define CP_REG_STDI_CH_ADDR 0xB1 +#define CP_STDI_DVALID_CH1_BMSK 0x0080 +#define CP_STDI_DVALID_CH1_SHFT 7 + +/* SDP R/O Main Map Registers */ +#define SDP_RO_MAIN_STATUS1_ADDR 0x10 +#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 +#define SDP_RO_MAIN_COL_KILL_SHFT 7 +#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 +#define SDP_RO_MAIN_AD_RESULT_SHFT 4 +#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 +#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 +#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 +#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 +#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 +#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 +#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 +#define SDP_RO_MAIN_IN_LOCK_SHFT 0 + +/* + * CSI Map Registers + */ +#define CSI_REG_TX_CFG1_ADDR 0x00 +#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 +#define CSI_CTRL_TX_PWRDN_SHFT 7 +#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 +#define CSI_CTRL_AUTO_PARAMS_SHFT 5 +#define CSI_CTRL_NUM_LANES_BMSK 0x0007 +#define CSI_CTRL_NUM_LANES_SHFT 0 + +#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 +#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 +#define CSI_CTRL_DPHY_PWDN_SHFT 0 + +enum adv7481_adresult { + AD_NTSM_M_J = 0x0, + AD_NTSC_4_43 = 0x1, + AD_PAL_M = 0x2, + AD_PAL_60 = 0x3, + AD_PAL_B_G = 0x4, + AD_SECAM = 0x5, + AD_PAL_COMB_N = 0x6, + AD_SECAM_525 = 0x7, +}; + +enum adv7481_color_depth { + CD_8BIT = 0x0, + CD_10BIT = 0x1, + CD_12BIT = 0x2, + CD_16BIT = 0x3, +}; + +enum adv7481_intrq_dur_sel { + AD_4_XTAL_PER = 0x0, + AD_16_XTAL_PER = 0x1, + AD_64_XTAL_PER = 0x2, + AD_ACTIVE_UNTIL_CLR = 0x3, +}; + +enum adv7481_intrq_op_sel { + AD_OP_OPEN_DRAIN = 0x0, + AD_OP_DRIVE_LOW = 0x1, + AD_OP_DRIVE_HIGH = 0x2, + AD_OP_DISABLED = 0x3, +}; + +enum adv7481_drv_llc_pad { + AD_LLC_PAD_NOT_USED = 0x0, + AD_MIN_DRIVE_STRNGTH = 0x1, + AD_MID_DRIVE_STRNGTH = 0x2, + AD_MAX_DRIVE_STRNGTH = 0x3, +}; + +#endif diff --git a/include/media/adv7481.h b/include/media/adv7481.h new file mode 100644 index 000000000000..80b8ee879ea4 --- /dev/null +++ b/include/media/adv7481.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ + +#ifndef __ADV7481_H__ +#define __ADV7481_H__ + +/** + * adv7481_platform_data + * structure to pass board specific information to the ADV7481 driver + * @rstb_gpio: put active low to hold chip in reset state + * @pwdnb_gpio: put active low to allow chip to power-down and disable I2C + * @irq_gpio : active low to trigger IRQ1 + * @irq2_gpio : active low to trigger IRQ2 + * @irq3_gpio : active low to trigger IRQ3 + * @i2c_csi_txa: CSI TXA I2C Map Address + * @i2c_csi_txb; CSI TXB I2C Map Address + * @i2c_hdmi: hdmi I2C Map Address + * @i2c_cp: cp I2C Map Address + * @i2c_sdp: sdp I2C Map Address + */ +struct adv7481_platform_data { + int rstb_gpio; + int pwdnb_gpio; + int irq1_gpio; + int irq2_gpio; + int irq3_gpio; + int i2c_csi_txa; + int i2c_csi_txb; + int i2c_hdmi; + int i2c_cp; + int i2c_sdp; + int i2c_rep; + +}; + + +/* + * Mode of operation. + * This is used as the input argument of the s_routing video op. + */ +enum adv7481_input { + ADV7481_IP_CVBS_1 = 0, + ADV7481_IP_CVBS_2, + ADV7481_IP_CVBS_3, + ADV7481_IP_CVBS_4, + ADV7481_IP_CVBS_5, + ADV7481_IP_CVBS_6, + ADV7481_IP_CVBS_7, + ADV7481_IP_CVBS_8, + ADV7481_IP_HDMI, + ADV7481_IP_TTL, + ADV7481_IP_CVBS_1_HDMI_SIM, + ADV7481_IP_CVBS_2_HDMI_SIM, + ADV7481_IP_CVBS_3_HDMI_SIM, + ADV7481_IP_CVBS_4_HDMI_SIM, + ADV7481_IP_CVBS_5_HDMI_SIM, + ADV7481_IP_CVBS_6_HDMI_SIM, + ADV7481_IP_CVBS_7_HDMI_SIM, + ADV7481_IP_CVBS_8_HDMI_SIM, + ADV7481_IP_NONE, + ADV7481_IP_INVALID, +}; +/* + * CSI Tx + * This is used as the output argument of the s_routing video op. + */ +enum adv7481_output { + ADV7481_OP_CSIA, + ADV7481_OP_CSIB, + ADV7481_OP_TTL +}; +enum adv7481_csi_lane_en { + ADV7481_CSI_1LANE_EN = 0x1, + ADV7481_CSI_4LANE_EN = 0x2, +}; +enum adv7481_mipi_lane { + ADV7481_MIPI_1LANE = 0x1, + ADV7481_MIPI_2LANE = 0x2, + ADV7481_MIPI_4LANE = 0x4, +}; +enum adv7481_csi_params { + ADV7481_AUTO_PARAMS = 0x1, +}; +#endif