1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036 |
- /* Copyright (c) 2010,2012, 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 <linux/i2c.h>
- #include <linux/types.h>
- #include <linux/bitops.h>
- #include <linux/adv7520.h>
- #include <linux/time.h>
- #include <linux/completion.h>
- #include <linux/wakelock.h>
- #include <linux/clk.h>
- #include <linux/pm_qos.h>
- #include <asm/atomic.h>
- #include <mach/cpuidle.h>
- #include "msm_fb.h"
- #define DEBUG
- #define DEV_DBG_PREFIX "HDMI: "
- #include "external_common.h"
- /* #define PORT_DEBUG */
- /* #define TESTING_FORCE_480p */
- #define HPD_DUTY_CYCLE 4 /*secs*/
- static struct external_common_state_type hdmi_common;
- static struct i2c_client *hclient;
- static struct clk *tv_enc_clk;
- static bool chip_power_on = FALSE; /* For chip power on/off */
- static bool enable_5v_on = FALSE;
- static bool hpd_power_on = FALSE;
- static atomic_t comm_power_on; /* For dtv power on/off (I2C) */
- static int suspend_count;
- static u8 reg[256]; /* HDMI panel registers */
- struct hdmi_data {
- struct msm_hdmi_platform_data *pd;
- struct work_struct isr_work;
- };
- static struct hdmi_data *dd;
- static struct work_struct hpd_timer_work;
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- static struct work_struct hdcp_handle_work;
- static int hdcp_activating;
- static DEFINE_MUTEX(hdcp_state_mutex);
- static int has_hdcp_hw_support = true;
- #endif
- static struct timer_list hpd_timer;
- static struct timer_list hpd_duty_timer;
- static struct work_struct hpd_duty_work;
- static unsigned int monitor_sense;
- static boolean hpd_cable_chg_detected;
- static struct pm_qos_request pm_qos_req;
- /* Change HDMI state */
- static void change_hdmi_state(int online)
- {
- if (!external_common_state)
- return;
- mutex_lock(&external_common_state_hpd_mutex);
- external_common_state->hpd_state = online;
- mutex_unlock(&external_common_state_hpd_mutex);
- if (!external_common_state->uevent_kobj)
- return;
- if (online) {
- kobject_uevent(external_common_state->uevent_kobj,
- KOBJ_ONLINE);
- switch_set_state(&external_common_state->sdev, 1);
- } else {
- kobject_uevent(external_common_state->uevent_kobj,
- KOBJ_OFFLINE);
- switch_set_state(&external_common_state->sdev, 0);
- }
- DEV_INFO("adv7520_uevent: %d [suspend# %d]\n", online, suspend_count);
- }
- /*
- * Read a value from a register on ADV7520 device
- * If sucessfull returns value read , otherwise error.
- */
- static u8 adv7520_read_reg(struct i2c_client *client, u8 reg)
- {
- int err;
- struct i2c_msg msg[2];
- u8 reg_buf[] = { reg };
- u8 data_buf[] = { 0 };
- if (!client->adapter)
- return -ENODEV;
- if (!atomic_read(&comm_power_on)) {
- DEV_WARN("%s: WARN: missing GPIO power\n", __func__);
- return -ENODEV;
- }
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 1;
- msg[0].buf = reg_buf;
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = 1;
- msg[1].buf = data_buf;
- err = i2c_transfer(client->adapter, msg, 2);
- if (err < 0) {
- DEV_INFO("%s: I2C err: %d\n", __func__, err);
- return err;
- }
- #ifdef PORT_DEBUG
- DEV_INFO("HDMI[%02x] [R] %02x\n", reg, data);
- #endif
- return *data_buf;
- }
- /*
- * Write a value to a register on adv7520 device.
- * Returns zero if successful, or non-zero otherwise.
- */
- static int adv7520_write_reg(struct i2c_client *client, u8 reg, u8 val)
- {
- int err;
- struct i2c_msg msg[1];
- unsigned char data[2];
- if (!client->adapter)
- return -ENODEV;
- if (!atomic_read(&comm_power_on)) {
- DEV_WARN("%s: WARN: missing GPIO power\n", __func__);
- return -ENODEV;
- }
- msg->addr = client->addr;
- msg->flags = 0;
- msg->len = 2;
- msg->buf = data;
- data[0] = reg;
- data[1] = val;
- err = i2c_transfer(client->adapter, msg, 1);
- if (err >= 0)
- return 0;
- #ifdef PORT_DEBUG
- DEV_INFO("HDMI[%02x] [W] %02x [%d]\n", reg, val, err);
- #endif
- return err;
- }
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- static void adv7520_close_hdcp_link(void)
- {
- if (!external_common_state->hdcp_active && !hdcp_activating)
- return;
- DEV_INFO("HDCP: Close link\n");
- reg[0xD5] = adv7520_read_reg(hclient, 0xD5);
- reg[0xD5] &= 0xFE;
- adv7520_write_reg(hclient, 0xD5, (u8)reg[0xD5]);
- reg[0x16] = adv7520_read_reg(hclient, 0x16);
- reg[0x16] &= 0xFE;
- adv7520_write_reg(hclient, 0x16, (u8)reg[0x16]);
- /* UnMute Audio */
- adv7520_write_reg(hclient, 0x0C, (u8)0x84);
- external_common_state->hdcp_active = FALSE;
- mutex_lock(&hdcp_state_mutex);
- hdcp_activating = FALSE;
- mutex_unlock(&hdcp_state_mutex);
- }
- static void adv7520_comm_power(int on, int show);
- static void adv7520_hdcp_enable(struct work_struct *work)
- {
- DEV_INFO("HDCP: Start reg[0xaf]=%02x (mute audio)\n", reg[0xaf]);
- adv7520_comm_power(1, 1);
- /* Mute Audio */
- adv7520_write_reg(hclient, 0x0C, (u8)0xC3);
- msleep(200);
- /* Wait for BKSV ready interrupt */
- /* Read BKSV's keys from HDTV */
- reg[0xBF] = adv7520_read_reg(hclient, 0xBF);
- reg[0xC0] = adv7520_read_reg(hclient, 0xC0);
- reg[0xC1] = adv7520_read_reg(hclient, 0xC1);
- reg[0xC2] = adv7520_read_reg(hclient, 0xC2);
- reg[0xc3] = adv7520_read_reg(hclient, 0xC3);
- DEV_DBG("HDCP: BKSV={%02x,%02x,%02x,%02x,%02x}\n", reg[0xbf], reg[0xc0],
- reg[0xc1], reg[0xc2], reg[0xc3]);
- /* Is SINK repeater */
- reg[0xBE] = adv7520_read_reg(hclient, 0xBE);
- if (~(reg[0xBE] & 0x40)) {
- ; /* compare with revocation list */
- /* Check 20 1's and 20 zero's */
- } else {
- /* Don't implement HDCP if sink as a repeater */
- adv7520_write_reg(hclient, 0x0C, (u8)0x84);
- mutex_lock(&hdcp_state_mutex);
- hdcp_activating = FALSE;
- mutex_unlock(&hdcp_state_mutex);
- DEV_WARN("HDCP: Sink Repeater (%02x), (unmute audio)\n",
- reg[0xbe]);
- adv7520_comm_power(0, 1);
- return;
- }
- msleep(200);
- reg[0xB8] = adv7520_read_reg(hclient, 0xB8);
- DEV_INFO("HDCP: Status reg[0xB8] is %02x\n", reg[0xb8]);
- if (reg[0xb8] & 0x40) {
- /* UnMute Audio */
- adv7520_write_reg(hclient, 0x0C, (u8)0x84);
- DEV_INFO("HDCP: A/V content Encrypted (unmute audio)\n");
- external_common_state->hdcp_active = TRUE;
- }
- adv7520_comm_power(0, 1);
- mutex_lock(&hdcp_state_mutex);
- hdcp_activating = FALSE;
- mutex_unlock(&hdcp_state_mutex);
- }
- #endif
- static int adv7520_read_edid_block(int block, uint8 *edid_buf)
- {
- u8 r = 0;
- int ret;
- struct i2c_msg msg[] = {
- { .addr = reg[0x43] >> 1,
- .flags = 0,
- .len = 1,
- .buf = &r },
- { .addr = reg[0x43] >> 1,
- .flags = I2C_M_RD,
- .len = 0x100,
- .buf = edid_buf } };
- if (block > 0)
- return 0;
- ret = i2c_transfer(hclient->adapter, msg, 2);
- DEV_DBG("EDID block: addr=%02x, ret=%d\n", reg[0x43] >> 1, ret);
- return (ret < 2) ? -ENODEV : 0;
- }
- static void adv7520_read_edid(void)
- {
- external_common_state->read_edid_block = adv7520_read_edid_block;
- if (hdmi_common_read_edid()) {
- u8 timeout;
- DEV_INFO("%s: retry\n", __func__);
- adv7520_write_reg(hclient, 0xc9, 0x13);
- msleep(500);
- timeout = (adv7520_read_reg(hclient, 0x96) & (1 << 2));
- if (timeout) {
- hdmi_common_read_edid();
- }
- }
- }
- static void adv7520_chip_on(void)
- {
- if (!chip_power_on) {
- /* Get the current register holding the power bit. */
- unsigned long reg0xaf = adv7520_read_reg(hclient, 0xaf);
- dd->pd->core_power(1, 1);
- /* Set the HDMI select bit. */
- set_bit(1, ®0xaf);
- DEV_INFO("%s: turn on chip power\n", __func__);
- adv7520_write_reg(hclient, 0x41, 0x10);
- adv7520_write_reg(hclient, 0xaf, (u8)reg0xaf);
- chip_power_on = TRUE;
- } else
- DEV_INFO("%s: chip already has power\n", __func__);
- }
- static void adv7520_chip_off(void)
- {
- if (chip_power_on) {
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- if (has_hdcp_hw_support)
- adv7520_close_hdcp_link();
- #endif
- DEV_INFO("%s: turn off chip power\n", __func__);
- adv7520_write_reg(hclient, 0x41, 0x50);
- dd->pd->core_power(0, 1);
- chip_power_on = FALSE;
- } else
- DEV_INFO("%s: chip is already off\n", __func__);
- monitor_sense = 0;
- hpd_cable_chg_detected = FALSE;
- if (enable_5v_on) {
- dd->pd->enable_5v(0);
- enable_5v_on = FALSE;
- }
- }
- /* Power ON/OFF ADV7520 chip */
- static void adv7520_isr_w(struct work_struct *work);
- static void adv7520_comm_power(int on, int show)
- {
- if (!on)
- atomic_dec(&comm_power_on);
- dd->pd->comm_power(on, 0/*show*/);
- if (on)
- atomic_inc(&comm_power_on);
- }
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- static void adv7520_start_hdcp(void);
- #endif
- static int adv7520_power_on(struct platform_device *pdev)
- {
- struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
- clk_prepare_enable(tv_enc_clk);
- external_common_state->dev = &pdev->dev;
- if (mfd != NULL) {
- DEV_INFO("adv7520_power: ON (%dx%d %d)\n",
- mfd->var_xres, mfd->var_yres, mfd->var_pixclock);
- hdmi_common_get_video_format_from_drv_data(mfd);
- }
- adv7520_comm_power(1, 1);
- /* Check if HPD is signaled */
- if (adv7520_read_reg(hclient, 0x42) & (1 << 6)) {
- DEV_INFO("power_on: cable detected\n");
- monitor_sense = adv7520_read_reg(hclient, 0xC6);
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- if (has_hdcp_hw_support) {
- if (!hdcp_activating)
- adv7520_start_hdcp();
- }
- #endif
- } else
- DEV_INFO("power_on: cable NOT detected\n");
- adv7520_comm_power(0, 1);
- pm_qos_update_request(&pm_qos_req, msm_cpuidle_get_deep_idle_latency());
- return 0;
- }
- static int adv7520_power_off(struct platform_device *pdev)
- {
- DEV_INFO("power_off\n");
- adv7520_comm_power(1, 1);
- adv7520_chip_off();
- pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE);
- adv7520_comm_power(0, 1);
- clk_disable_unprepare(tv_enc_clk);
- return 0;
- }
- /* AV7520 chip specific initialization */
- static void adv7520_chip_init(void)
- {
- /* Initialize the variables used to read/write the ADV7520 chip. */
- memset(®, 0xff, sizeof(reg));
- /* Get the values from the "Fixed Registers That Must Be Set". */
- reg[0x98] = adv7520_read_reg(hclient, 0x98);
- reg[0x9c] = adv7520_read_reg(hclient, 0x9c);
- reg[0x9d] = adv7520_read_reg(hclient, 0x9d);
- reg[0xa2] = adv7520_read_reg(hclient, 0xa2);
- reg[0xa3] = adv7520_read_reg(hclient, 0xa3);
- reg[0xde] = adv7520_read_reg(hclient, 0xde);
- /* Get the "HDMI/DVI Selection" register. */
- reg[0xaf] = adv7520_read_reg(hclient, 0xaf);
- /* Read Packet Memory I2C Address */
- reg[0x45] = adv7520_read_reg(hclient, 0x45);
- /* Hard coded values provided by ADV7520 data sheet. */
- reg[0x98] = 0x03;
- reg[0x9c] = 0x38;
- reg[0x9d] = 0x61;
- reg[0xa2] = 0x94;
- reg[0xa3] = 0x94;
- reg[0xde] = 0x88;
- /* Set the HDMI select bit. */
- reg[0xaf] |= 0x16;
- /* Set the audio related registers. */
- reg[0x01] = 0x00;
- reg[0x02] = 0x2d;
- reg[0x03] = 0x80;
- reg[0x0a] = 0x4d;
- reg[0x0b] = 0x0e;
- reg[0x0c] = 0x84;
- reg[0x0d] = 0x10;
- reg[0x12] = 0x00;
- reg[0x14] = 0x00;
- reg[0x15] = 0x20;
- reg[0x44] = 0x79;
- reg[0x73] = 0x01;
- reg[0x76] = 0x00;
- /* Set 720p display related registers */
- reg[0x16] = 0x00;
- reg[0x18] = 0x46;
- reg[0x55] = 0x00;
- reg[0x3c] = 0x04;
- /* Set Interrupt Mask register for HPD/HDCP */
- reg[0x94] = 0xC0;
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- if (has_hdcp_hw_support)
- reg[0x95] = 0xC0;
- else
- reg[0x95] = 0x00;
- #else
- reg[0x95] = 0x00;
- #endif
- adv7520_write_reg(hclient, 0x94, reg[0x94]);
- adv7520_write_reg(hclient, 0x95, reg[0x95]);
- /* Set Packet Memory I2C Address */
- reg[0x45] = 0x74;
- /* Set the values from the "Fixed Registers That Must Be Set". */
- adv7520_write_reg(hclient, 0x98, reg[0x98]);
- adv7520_write_reg(hclient, 0x9c, reg[0x9c]);
- adv7520_write_reg(hclient, 0x9d, reg[0x9d]);
- adv7520_write_reg(hclient, 0xa2, reg[0xa2]);
- adv7520_write_reg(hclient, 0xa3, reg[0xa3]);
- adv7520_write_reg(hclient, 0xde, reg[0xde]);
- /* Set the "HDMI/DVI Selection" register. */
- adv7520_write_reg(hclient, 0xaf, reg[0xaf]);
- /* Set EDID Monitor address */
- reg[0x43] = 0x7E;
- adv7520_write_reg(hclient, 0x43, reg[0x43]);
- /* Enable the i2s audio input. */
- adv7520_write_reg(hclient, 0x01, reg[0x01]);
- adv7520_write_reg(hclient, 0x02, reg[0x02]);
- adv7520_write_reg(hclient, 0x03, reg[0x03]);
- adv7520_write_reg(hclient, 0x0a, reg[0x0a]);
- adv7520_write_reg(hclient, 0x0b, reg[0x0b]);
- adv7520_write_reg(hclient, 0x0c, reg[0x0c]);
- adv7520_write_reg(hclient, 0x0d, reg[0x0d]);
- adv7520_write_reg(hclient, 0x12, reg[0x12]);
- adv7520_write_reg(hclient, 0x14, reg[0x14]);
- adv7520_write_reg(hclient, 0x15, reg[0x15]);
- adv7520_write_reg(hclient, 0x44, reg[0x44]);
- adv7520_write_reg(hclient, 0x73, reg[0x73]);
- adv7520_write_reg(hclient, 0x76, reg[0x76]);
- /* Enable 720p display */
- adv7520_write_reg(hclient, 0x16, reg[0x16]);
- adv7520_write_reg(hclient, 0x18, reg[0x18]);
- adv7520_write_reg(hclient, 0x55, reg[0x55]);
- adv7520_write_reg(hclient, 0x3c, reg[0x3c]);
- /* Set Packet Memory address to avoid conflict
- with Bosch Accelerometer */
- adv7520_write_reg(hclient, 0x45, reg[0x45]);
- /* Ensure chip is in low-power state */
- adv7520_write_reg(hclient, 0x41, 0x50);
- }
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- static void adv7520_start_hdcp(void)
- {
- mutex_lock(&hdcp_state_mutex);
- if (hdcp_activating) {
- DEV_WARN("adv7520_timer: HDCP already"
- " activating, skipping\n");
- mutex_unlock(&hdcp_state_mutex);
- return;
- }
- hdcp_activating = TRUE;
- mutex_unlock(&hdcp_state_mutex);
- del_timer(&hpd_duty_timer);
- adv7520_comm_power(1, 1);
- if (!enable_5v_on) {
- dd->pd->enable_5v(1);
- enable_5v_on = TRUE;
- adv7520_chip_on();
- }
- /* request for HDCP */
- reg[0xaf] = adv7520_read_reg(hclient, 0xaf);
- reg[0xaf] |= 0x90;
- adv7520_write_reg(hclient, 0xaf, reg[0xaf]);
- reg[0xaf] = adv7520_read_reg(hclient, 0xaf);
- reg[0xba] = adv7520_read_reg(hclient, 0xba);
- reg[0xba] |= 0x10;
- adv7520_write_reg(hclient, 0xba, reg[0xba]);
- reg[0xba] = adv7520_read_reg(hclient, 0xba);
- adv7520_comm_power(0, 1);
- DEV_INFO("HDCP: reg[0xaf]=0x%02x, reg[0xba]=0x%02x, waiting for BKSV\n",
- reg[0xaf], reg[0xba]);
- /* will check for HDCP Error or BKSV ready */
- mod_timer(&hpd_duty_timer, jiffies + HZ/2);
- }
- #endif
- static void adv7520_hpd_timer_w(struct work_struct *work)
- {
- if (!external_common_state->hpd_feature_on) {
- DEV_INFO("adv7520_timer: skipping, feature off\n");
- return;
- }
- if ((monitor_sense & 0x4) && !external_common_state->hpd_state) {
- int timeout;
- DEV_DBG("adv7520_timer: Cable Detected\n");
- adv7520_comm_power(1, 1);
- adv7520_chip_on();
- if (hpd_cable_chg_detected) {
- hpd_cable_chg_detected = FALSE;
- /* Ensure 5V to read EDID */
- if (!enable_5v_on) {
- dd->pd->enable_5v(1);
- enable_5v_on = TRUE;
- }
- msleep(500);
- timeout = (adv7520_read_reg(hclient, 0x96) & (1 << 2));
- if (timeout) {
- DEV_DBG("adv7520_timer: EDID-Ready..\n");
- adv7520_read_edid();
- } else
- DEV_DBG("adv7520_timer: EDID TIMEOUT (C9=%02x)"
- "\n", adv7520_read_reg(hclient, 0xC9));
- }
- #ifdef TESTING_FORCE_480p
- external_common_state->disp_mode_list.num_of_elements = 1;
- external_common_state->disp_mode_list.disp_mode_list[0] =
- HDMI_VFRMT_720x480p60_16_9;
- #endif
- adv7520_comm_power(0, 1);
- #ifndef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- /* HDMI_5V_EN not needed anymore */
- if (enable_5v_on) {
- DEV_DBG("adv7520_timer: EDID done, no HDCP, 5V not "
- "needed anymore\n");
- dd->pd->enable_5v(0);
- enable_5v_on = FALSE;
- }
- #endif
- change_hdmi_state(1);
- } else if (external_common_state->hpd_state) {
- adv7520_comm_power(1, 1);
- adv7520_chip_off();
- adv7520_comm_power(0, 1);
- DEV_DBG("adv7520_timer: Cable Removed\n");
- change_hdmi_state(0);
- }
- }
- static void adv7520_hpd_timer_f(unsigned long data)
- {
- schedule_work(&hpd_timer_work);
- }
- static void adv7520_isr_w(struct work_struct *work)
- {
- static int state_count;
- static u8 last_reg0x96;
- u8 reg0xc8;
- u8 reg0x96;
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- static u8 last_reg0x97;
- u8 reg0x97 = 0;
- #endif
- if (!external_common_state->hpd_feature_on) {
- DEV_DBG("adv7520_irq: skipping, hpd off\n");
- return;
- }
- adv7520_comm_power(1, 1);
- reg0x96 = adv7520_read_reg(hclient, 0x96);
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- if (has_hdcp_hw_support) {
- reg0x97 = adv7520_read_reg(hclient, 0x97);
- /* Clearing the Interrupts */
- adv7520_write_reg(hclient, 0x97, reg0x97);
- }
- #endif
- /* Clearing the Interrupts */
- adv7520_write_reg(hclient, 0x96, reg0x96);
- if ((reg0x96 == 0xC0) || (reg0x96 & 0x40)) {
- #ifdef DEBUG
- unsigned int hpd_state = adv7520_read_reg(hclient, 0x42);
- #endif
- monitor_sense = adv7520_read_reg(hclient, 0xC6);
- DEV_DBG("adv7520_irq: reg[0x42]=%02x && reg[0xC6]=%02x\n",
- hpd_state, monitor_sense);
- if (!enable_5v_on) {
- dd->pd->enable_5v(1);
- enable_5v_on = TRUE;
- }
- if (!hpd_power_on) {
- dd->pd->core_power(1, 1);
- hpd_power_on = TRUE;
- }
- /* Timer for catching interrupt debouning */
- DEV_DBG("adv7520_irq: Timer in .5sec\n");
- hpd_cable_chg_detected = TRUE;
- mod_timer(&hpd_timer, jiffies + HZ/2);
- }
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- if (has_hdcp_hw_support) {
- if (hdcp_activating) {
- /* HDCP controller error Interrupt */
- if (reg0x97 & 0x80) {
- DEV_ERR("adv7520_irq: HDCP_ERROR\n");
- state_count = 0;
- adv7520_close_hdcp_link();
- /* BKSV Ready interrupts */
- } else if (reg0x97 & 0x40) {
- DEV_INFO("adv7520_irq: BKSV keys ready, Begin"
- " HDCP encryption\n");
- state_count = 0;
- schedule_work(&hdcp_handle_work);
- } else if (++state_count > 2 && (monitor_sense & 0x4)) {
- DEV_INFO("adv7520_irq: Still waiting for BKSV,"
- "restart HDCP\n");
- hdcp_activating = FALSE;
- state_count = 0;
- adv7520_chip_off();
- adv7520_start_hdcp();
- }
- reg0xc8 = adv7520_read_reg(hclient, 0xc8);
- DEV_INFO("adv7520_irq: DDC controller reg[0xC8]=0x%02x,"
- "state_count=%d, monitor_sense=%x\n",
- reg0xc8, state_count, monitor_sense);
- } else if (!external_common_state->hdcp_active
- && (monitor_sense & 0x4)) {
- DEV_INFO("adv7520_irq: start HDCP with"
- " monitor sense\n");
- state_count = 0;
- adv7520_start_hdcp();
- } else
- state_count = 0;
- if (last_reg0x97 != reg0x97 || last_reg0x96 != reg0x96)
- DEV_DBG("adv7520_irq: reg[0x96]=%02x "
- "reg[0x97]=%02x: HDCP: %d\n", reg0x96, reg0x97,
- external_common_state->hdcp_active);
- last_reg0x97 = reg0x97;
- } else {
- if (last_reg0x96 != reg0x96)
- DEV_DBG("adv7520_irq: reg[0x96]=%02x\n", reg0x96);
- }
- #else
- if (last_reg0x96 != reg0x96)
- DEV_DBG("adv7520_irq: reg[0x96]=%02x\n", reg0x96);
- #endif
- last_reg0x96 = reg0x96;
- adv7520_comm_power(0, 1);
- }
- static void adv7520_hpd_duty_work(struct work_struct *work)
- {
- if (!external_common_state->hpd_feature_on) {
- DEV_WARN("%s: hpd feature is off, skipping\n", __func__);
- return;
- }
- dd->pd->core_power(1, 0);
- msleep(10);
- adv7520_isr_w(NULL);
- dd->pd->core_power(0, 0);
- }
- static void adv7520_hpd_duty_timer_f(unsigned long data)
- {
- if (!external_common_state->hpd_feature_on) {
- DEV_WARN("%s: hpd feature is off, skipping\n", __func__);
- return;
- }
- mod_timer(&hpd_duty_timer, jiffies + HPD_DUTY_CYCLE*HZ);
- schedule_work(&hpd_duty_work);
- }
- static const struct i2c_device_id adv7520_id[] = {
- { ADV7520_DRV_NAME , 0},
- {}
- };
- static struct msm_fb_panel_data hdmi_panel_data = {
- .on = adv7520_power_on,
- .off = adv7520_power_off,
- };
- static struct platform_device hdmi_device = {
- .name = ADV7520_DRV_NAME ,
- .id = 2,
- .dev = {
- .platform_data = &hdmi_panel_data,
- }
- };
- static void adv7520_ensure_init(void)
- {
- static boolean init_done;
- if (!init_done) {
- int rc = dd->pd->init_irq();
- if (rc) {
- DEV_ERR("adv7520_init: init_irq: %d\n", rc);
- return;
- }
- init_done = TRUE;
- }
- DEV_INFO("adv7520_init: chip init\n");
- adv7520_comm_power(1, 1);
- adv7520_chip_init();
- adv7520_comm_power(0, 1);
- }
- static int adv7520_hpd_feature(int on)
- {
- int rc = 0;
- if (!on) {
- if (enable_5v_on) {
- dd->pd->enable_5v(0);
- enable_5v_on = FALSE;
- }
- if (hpd_power_on) {
- dd->pd->core_power(0, 1);
- hpd_power_on = FALSE;
- }
- DEV_DBG("adv7520_hpd: %d: stop duty timer\n", on);
- del_timer(&hpd_timer);
- del_timer(&hpd_duty_timer);
- external_common_state->hpd_state = 0;
- }
- if (on) {
- dd->pd->core_power(1, 0);
- adv7520_ensure_init();
- adv7520_comm_power(1, 1);
- monitor_sense = adv7520_read_reg(hclient, 0xC6);
- DEV_DBG("adv7520_irq: reg[0xC6]=%02x\n", monitor_sense);
- adv7520_comm_power(0, 1);
- dd->pd->core_power(0, 0);
- if (monitor_sense & 0x4) {
- if (!enable_5v_on) {
- dd->pd->enable_5v(1);
- enable_5v_on = TRUE;
- }
- if (!hpd_power_on) {
- dd->pd->core_power(1, 1);
- hpd_power_on = TRUE;
- }
- hpd_cable_chg_detected = TRUE;
- mod_timer(&hpd_timer, jiffies + HZ/2);
- }
- DEV_DBG("adv7520_hpd: %d start duty timer\n", on);
- mod_timer(&hpd_duty_timer, jiffies + HZ/100);
- }
- DEV_INFO("adv7520_hpd: %d\n", on);
- return rc;
- }
- static int __devinit
- adv7520_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- int rc;
- struct platform_device *fb_dev;
- dd = kzalloc(sizeof *dd, GFP_KERNEL);
- if (!dd) {
- rc = -ENOMEM;
- goto probe_exit;
- }
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
- return -ENODEV;
- external_common_state->dev = &client->dev;
- /* Init real i2c_client */
- hclient = client;
- i2c_set_clientdata(client, dd);
- dd->pd = client->dev.platform_data;
- if (!dd->pd) {
- rc = -ENODEV;
- goto probe_free;
- }
- INIT_WORK(&dd->isr_work, adv7520_isr_w);
- INIT_WORK(&hpd_timer_work, adv7520_hpd_timer_w);
- #ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL_HDCP_SUPPORT
- if (dd->pd->check_hdcp_hw_support)
- has_hdcp_hw_support = dd->pd->check_hdcp_hw_support();
- if (has_hdcp_hw_support)
- INIT_WORK(&hdcp_handle_work, adv7520_hdcp_enable);
- else
- DEV_INFO("%s: no hdcp hw support.\n", __func__);
- #endif
- init_timer(&hpd_timer);
- hpd_timer.function = adv7520_hpd_timer_f;
- hpd_timer.data = (unsigned long)NULL;
- hpd_timer.expires = 0xffffffff;
- add_timer(&hpd_timer);
- external_common_state->hpd_feature = adv7520_hpd_feature;
- DEV_INFO("adv7520_probe: HPD detection on request\n");
- init_timer(&hpd_duty_timer);
- hpd_duty_timer.function = adv7520_hpd_duty_timer_f;
- hpd_duty_timer.data = (unsigned long)NULL;
- hpd_duty_timer.expires = 0xffffffff;
- add_timer(&hpd_duty_timer);
- INIT_WORK(&hpd_duty_work, adv7520_hpd_duty_work);
- DEV_INFO("adv7520_probe: HPD detection ON (duty)\n");
- fb_dev = msm_fb_add_device(&hdmi_device);
- if (fb_dev) {
- rc = external_common_state_create(fb_dev);
- if (rc)
- goto probe_free;
- } else
- DEV_ERR("adv7520_probe: failed to add fb device\n");
- if (hdmi_prim_display)
- external_common_state->sdev.name = "hdmi_as_primary";
- else
- external_common_state->sdev.name = "hdmi";
- if (switch_dev_register(&external_common_state->sdev) < 0)
- DEV_ERR("Hdmi switch registration failed\n");
- return 0;
- probe_free:
- kfree(dd);
- dd = NULL;
- probe_exit:
- return rc;
- }
- static int __devexit adv7520_remove(struct i2c_client *client)
- {
- if (!client->adapter) {
- DEV_ERR("%s: No HDMI Device\n", __func__);
- return -ENODEV;
- }
- switch_dev_unregister(&external_common_state->sdev);
- pm_qos_remove_request(&pm_qos_req);
- kfree(dd);
- dd = NULL;
- return 0;
- }
- #ifdef CONFIG_SUSPEND
- static int adv7520_i2c_suspend(struct device *dev)
- {
- DEV_INFO("%s\n", __func__);
- ++suspend_count;
- if (external_common_state->hpd_feature_on) {
- DEV_DBG("%s: stop duty timer\n", __func__);
- del_timer(&hpd_duty_timer);
- del_timer(&hpd_timer);
- }
- /* Turn off LDO8 and go into low-power state */
- if (chip_power_on) {
- DEV_DBG("%s: turn off power\n", __func__);
- adv7520_comm_power(1, 1);
- adv7520_write_reg(hclient, 0x41, 0x50);
- adv7520_comm_power(0, 1);
- dd->pd->core_power(0, 1);
- }
- return 0;
- }
- static int adv7520_i2c_resume(struct device *dev)
- {
- DEV_INFO("%s\n", __func__);
- /* Turn on LDO8 and go into normal-power state */
- if (chip_power_on) {
- DEV_DBG("%s: turn on power\n", __func__);
- dd->pd->core_power(1, 1);
- adv7520_comm_power(1, 1);
- adv7520_write_reg(hclient, 0x41, 0x10);
- adv7520_comm_power(0, 1);
- }
- if (external_common_state->hpd_feature_on) {
- DEV_DBG("%s: start duty timer\n", __func__);
- mod_timer(&hpd_duty_timer, jiffies + HPD_DUTY_CYCLE*HZ);
- }
- return 0;
- }
- #else
- #define adv7520_i2c_suspend NULL
- #define adv7520_i2c_resume NULL
- #endif
- static const struct dev_pm_ops adv7520_device_pm_ops = {
- .suspend = adv7520_i2c_suspend,
- .resume = adv7520_i2c_resume,
- };
- static struct i2c_driver hdmi_i2c_driver = {
- .driver = {
- .name = ADV7520_DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &adv7520_device_pm_ops,
- },
- .probe = adv7520_probe,
- .id_table = adv7520_id,
- .remove = __devexit_p(adv7520_remove),
- };
- static int __init adv7520_init(void)
- {
- int rc;
- pr_info("%s\n", __func__);
- external_common_state = &hdmi_common;
- external_common_state->video_resolution = HDMI_VFRMT_1280x720p60_16_9;
- tv_enc_clk = clk_get(NULL, "tv_enc_clk");
- if (IS_ERR(tv_enc_clk)) {
- printk(KERN_ERR "error: can't get tv_enc_clk!\n");
- return IS_ERR(tv_enc_clk);
- }
- HDMI_SETUP_LUT(640x480p60_4_3); /* 25.20MHz */
- HDMI_SETUP_LUT(720x480p60_16_9); /* 27.03MHz */
- HDMI_SETUP_LUT(1280x720p60_16_9); /* 74.25MHz */
- HDMI_SETUP_LUT(720x576p50_16_9); /* 27.00MHz */
- HDMI_SETUP_LUT(1280x720p50_16_9); /* 74.25MHz */
- hdmi_common_init_panel_info(&hdmi_panel_data.panel_info);
- rc = i2c_add_driver(&hdmi_i2c_driver);
- if (rc) {
- pr_err("hdmi_init FAILED: i2c_add_driver rc=%d\n", rc);
- goto init_exit;
- }
- if (machine_is_msm7x30_surf() || machine_is_msm8x55_surf()) {
- short *hdtv_mux = (short *)ioremap(0x8e000170 , 0x100);
- *hdtv_mux++ = 0x020b;
- *hdtv_mux = 0x8000;
- iounmap(hdtv_mux);
- }
- pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
- return 0;
- init_exit:
- if (tv_enc_clk)
- clk_put(tv_enc_clk);
- return rc;
- }
- static void __exit adv7520_exit(void)
- {
- i2c_del_driver(&hdmi_i2c_driver);
- }
- module_init(adv7520_init);
- module_exit(adv7520_exit);
- MODULE_LICENSE("GPL v2");
- MODULE_VERSION("0.1");
- MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
- MODULE_DESCRIPTION("ADV7520 HDMI driver");
|