1721 lines
45 KiB
C
1721 lines
45 KiB
C
/* Copyright (c) 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/types.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/mutex.h>
|
|
#include <mach/msm_hdmi_audio.h>
|
|
#include <mach/clk.h>
|
|
#include <mach/msm_iomap.h>
|
|
#include <mach/socinfo.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/mhl_8334.h>
|
|
|
|
#include "msm_fb.h"
|
|
#include "external_common.h"
|
|
#include "hdmi_msm.h"
|
|
#include "mhl_i2c_utils.h"
|
|
|
|
#define MSC_START_BIT_MSC_CMD (0x01 << 0)
|
|
#define MSC_START_BIT_VS_CMD (0x01 << 1)
|
|
#define MSC_START_BIT_READ_REG (0x01 << 2)
|
|
#define MSC_START_BIT_WRITE_REG (0x01 << 3)
|
|
#define MSC_START_BIT_WRITE_BURST (0x01 << 4)
|
|
|
|
static struct i2c_device_id mhl_sii_i2c_id[] = {
|
|
{ MHL_DRIVER_NAME, 0 },
|
|
{ }
|
|
};
|
|
|
|
struct mhl_msm_state_t *mhl_msm_state;
|
|
spinlock_t mhl_state_lock;
|
|
struct workqueue_struct *msc_send_workqueue;
|
|
|
|
static int mhl_i2c_probe(struct i2c_client *client,\
|
|
const struct i2c_device_id *id);
|
|
static int mhl_i2c_remove(struct i2c_client *client);
|
|
static void force_usb_switch_open(void);
|
|
static void release_usb_switch_open(void);
|
|
static void switch_mode(enum mhl_st_type to_mode);
|
|
static irqreturn_t mhl_tx_isr(int irq, void *dev_id);
|
|
void (*notify_usb_online)(int online);
|
|
static void mhl_drive_hpd(uint8_t to_state);
|
|
static int mhl_send_msc_command(struct msc_command_struct *req);
|
|
static void list_cmd_put(struct msc_command_struct *cmd);
|
|
static struct msc_command_struct *list_cmd_get(void);
|
|
static void mhl_msc_send_work(struct work_struct *work);
|
|
|
|
static struct i2c_driver mhl_sii_i2c_driver = {
|
|
.driver = {
|
|
.name = MHL_DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = mhl_i2c_probe,
|
|
/*.remove = __exit_p(mhl_i2c_remove),*/
|
|
.remove = mhl_i2c_remove,
|
|
.id_table = mhl_sii_i2c_id,
|
|
};
|
|
|
|
static void mhl_sii_reset_pin(int on)
|
|
{
|
|
gpio_set_value(mhl_msm_state->mhl_data->gpio_mhl_reset, on);
|
|
return;
|
|
}
|
|
|
|
static int mhl_sii_reg_enable(void)
|
|
{
|
|
static struct regulator *reg_8038_l20;
|
|
static struct regulator *reg_8038_l11;
|
|
int rc;
|
|
|
|
pr_debug("Inside %s\n", __func__);
|
|
if (!reg_8038_l20) {
|
|
reg_8038_l20 = regulator_get(&mhl_msm_state->i2c_client->dev,
|
|
"mhl_avcc12");
|
|
if (IS_ERR(reg_8038_l20)) {
|
|
pr_err("could not get reg_8038_l20, rc = %ld\n",
|
|
PTR_ERR(reg_8038_l20));
|
|
return -ENODEV;
|
|
}
|
|
rc = regulator_enable(reg_8038_l20);
|
|
if (rc) {
|
|
pr_err("'%s' regulator enable failed, rc=%d\n",
|
|
"mhl_l20", rc);
|
|
return rc;
|
|
} else
|
|
pr_debug("REGULATOR L20 ENABLED\n");
|
|
}
|
|
|
|
if (!reg_8038_l11) {
|
|
reg_8038_l11 = regulator_get(&mhl_msm_state->i2c_client->dev,
|
|
"mhl_iovcc18");
|
|
if (IS_ERR(reg_8038_l11)) {
|
|
pr_err("could not get reg_8038_l11, rc = %ld\n",
|
|
PTR_ERR(reg_8038_l11));
|
|
return -ENODEV;
|
|
}
|
|
rc = regulator_enable(reg_8038_l11);
|
|
if (rc) {
|
|
pr_err("'%s' regulator enable failed, rc=%d\n",
|
|
"mhl_l11", rc);
|
|
return rc;
|
|
} else
|
|
pr_debug("REGULATOR L11 ENABLED\n");
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static void mhl_sii_power_on(void)
|
|
{
|
|
int ret;
|
|
pr_debug("MHL SII POWER ON\n");
|
|
if (!mhl_msm_state->mhl_data->gpio_mhl_power) {
|
|
pr_warn("%s: no power reqd for this platform\n", __func__);
|
|
return;
|
|
}
|
|
|
|
ret = gpio_request(mhl_msm_state->mhl_data->gpio_mhl_power, "W_PWR");
|
|
if (ret < 0) {
|
|
pr_err("MHL_POWER_GPIO req failed: %d\n",
|
|
ret);
|
|
return;
|
|
}
|
|
ret = gpio_direction_output(mhl_msm_state->mhl_data->gpio_mhl_power,
|
|
1);
|
|
if (ret < 0) {
|
|
pr_err(
|
|
"SET GPIO MHL_POWER_GPIO direction failed: %d\n",
|
|
ret);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_power);
|
|
return;
|
|
}
|
|
gpio_set_value(mhl_msm_state->mhl_data->gpio_mhl_power, 1);
|
|
|
|
if (mhl_sii_reg_enable())
|
|
pr_err("Regulator enable failed\n");
|
|
|
|
pr_debug("MHL SII POWER ON Successful\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Request for GPIO allocations
|
|
* Set appropriate GPIO directions
|
|
*/
|
|
static int mhl_sii_gpio_setup(int on)
|
|
{
|
|
int ret;
|
|
if (on) {
|
|
if (mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux) {
|
|
ret = gpio_request(mhl_msm_state->\
|
|
mhl_data->gpio_hdmi_mhl_mux, "W_MUX");
|
|
if (ret < 0) {
|
|
pr_err("GPIO HDMI_MHL MUX req failed:%d\n",
|
|
ret);
|
|
return -EBUSY;
|
|
}
|
|
ret = gpio_direction_output(
|
|
mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux, 0);
|
|
if (ret < 0) {
|
|
pr_err("SET GPIO HDMI_MHL dir failed:%d\n",
|
|
ret);
|
|
gpio_free(mhl_msm_state->\
|
|
mhl_data->gpio_hdmi_mhl_mux);
|
|
return -EBUSY;
|
|
}
|
|
msleep(50);
|
|
gpio_set_value(mhl_msm_state->\
|
|
mhl_data->gpio_hdmi_mhl_mux, 0);
|
|
pr_debug("SET GPIO HDMI MHL MUX %d to 0\n",
|
|
mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux);
|
|
}
|
|
|
|
ret = gpio_request(mhl_msm_state->mhl_data->gpio_mhl_reset,
|
|
"W_RST#");
|
|
if (ret < 0) {
|
|
pr_err("GPIO RESET request failed: %d\n", ret);
|
|
return -EBUSY;
|
|
}
|
|
ret = gpio_direction_output(mhl_msm_state->\
|
|
mhl_data->gpio_mhl_reset, 1);
|
|
if (ret < 0) {
|
|
pr_err("SET GPIO RESET direction failed: %d\n", ret);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_reset);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux);
|
|
return -EBUSY;
|
|
}
|
|
ret = gpio_request(mhl_msm_state->mhl_data->gpio_mhl_int,
|
|
"W_INT");
|
|
if (ret < 0) {
|
|
pr_err("GPIO INT request failed: %d\n", ret);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_reset);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux);
|
|
return -EBUSY;
|
|
}
|
|
ret = gpio_direction_input(mhl_msm_state->\
|
|
mhl_data->gpio_mhl_int);
|
|
if (ret < 0) {
|
|
pr_err("SET GPIO INTR direction failed: %d\n", ret);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_reset);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_int);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux);
|
|
return -EBUSY;
|
|
}
|
|
} else {
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_reset);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_int);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_hdmi_mhl_mux);
|
|
gpio_free(mhl_msm_state->mhl_data->gpio_mhl_power);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* USB_HANDSHAKING FUNCTIONS */
|
|
|
|
int mhl_device_discovery(const char *name, int *result)
|
|
|
|
{
|
|
int timeout ;
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27);
|
|
msleep(50);
|
|
if (mhl_msm_state->cur_state == POWER_STATE_D3) {
|
|
/* give MHL driver chance to handle RGND interrupt */
|
|
INIT_COMPLETION(mhl_msm_state->rgnd_done);
|
|
timeout = wait_for_completion_interruptible_timeout
|
|
(&mhl_msm_state->rgnd_done, HZ/2);
|
|
if (!timeout) {
|
|
/* most likely nothing plugged in USB */
|
|
/* USB HOST connected or already in USB mode */
|
|
pr_debug("Timedout Returning from discovery mode\n");
|
|
*result = MHL_DISCOVERY_RESULT_USB;
|
|
return 0;
|
|
}
|
|
*result = mhl_msm_state->mhl_mode ?
|
|
MHL_DISCOVERY_RESULT_MHL : MHL_DISCOVERY_RESULT_USB;
|
|
} else
|
|
/* not in D3. already in MHL mode */
|
|
*result = MHL_DISCOVERY_RESULT_MHL;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mhl_device_discovery);
|
|
|
|
int mhl_register_callback(const char *name, void (*callback)(int online))
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
if (!callback)
|
|
return -EINVAL;
|
|
if (!notify_usb_online)
|
|
notify_usb_online = callback;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mhl_register_callback);
|
|
|
|
int mhl_unregister_callback(const char *name)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
if (notify_usb_online)
|
|
notify_usb_online = NULL;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mhl_unregister_callback);
|
|
|
|
|
|
static void cbus_reset(void)
|
|
{
|
|
uint8_t i;
|
|
|
|
/*
|
|
* REG_SRST
|
|
*/
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0000, BIT3, BIT3);
|
|
msleep(20);
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0000, BIT3, 0x00);
|
|
/*
|
|
* REG_INTR1 and REG_INTR4
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0075, BIT6);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0022,
|
|
BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
|
|
/* REG5 */
|
|
if (mhl_msm_state->chip_rev_id < 1)
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0024, BIT3 | BIT4);
|
|
else
|
|
/*REG5 Mask disabled due to auto FIFO reset ??*/
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0024, 0x00);
|
|
|
|
/* Unmask CBUS1 Intrs */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0009,
|
|
BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
|
|
|
|
/* Unmask CBUS2 Intrs */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x001F, BIT2 | BIT3);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
/*
|
|
* Enable WRITE_STAT interrupt for writes to
|
|
* all 4 MSC Status registers.
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, (0xE0 + i), 0xFF);
|
|
|
|
/*
|
|
* Enable SET_INT interrupt for writes to
|
|
* all 4 MSC Interrupt registers.
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, (0xF0 + i), 0xFF);
|
|
}
|
|
}
|
|
|
|
static void init_cbus_regs(void)
|
|
{
|
|
uint8_t regval;
|
|
|
|
/* Increase DDC translation layer timer*/
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0007, 0xF2);
|
|
/* Drive High Time */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0036, 0x03);
|
|
/* Use programmed timing */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0039, 0x30);
|
|
/* CBUS Drive Strength */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0040, 0x03);
|
|
/*
|
|
* Write initial default settings
|
|
* to devcap regs: default settings
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEV_STATE,
|
|
DEVCAP_VAL_DEV_STATE);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_MHL_VERSION,
|
|
DEVCAP_VAL_MHL_VERSION);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEV_CAT,
|
|
DEVCAP_VAL_DEV_CAT);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_ADOPTER_ID_H,
|
|
DEVCAP_VAL_ADOPTER_ID_H);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_ADOPTER_ID_L,
|
|
DEVCAP_VAL_ADOPTER_ID_L);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_VID_LINK_MODE,
|
|
DEVCAP_VAL_VID_LINK_MODE);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_AUD_LINK_MODE,
|
|
DEVCAP_VAL_AUD_LINK_MODE);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_VIDEO_TYPE,
|
|
DEVCAP_VAL_VIDEO_TYPE);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_LOG_DEV_MAP,
|
|
DEVCAP_VAL_LOG_DEV_MAP);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_BANDWIDTH,
|
|
DEVCAP_VAL_BANDWIDTH);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_FEATURE_FLAG,
|
|
DEVCAP_VAL_FEATURE_FLAG);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEVICE_ID_H,
|
|
DEVCAP_VAL_DEVICE_ID_H);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_DEVICE_ID_L,
|
|
DEVCAP_VAL_DEVICE_ID_L);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_SCRATCHPAD_SIZE,
|
|
DEVCAP_VAL_SCRATCHPAD_SIZE);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_INT_STAT_SIZE,
|
|
DEVCAP_VAL_INT_STAT_SIZE);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0080 | DEVCAP_OFFSET_RESERVED,
|
|
DEVCAP_VAL_RESERVED);
|
|
|
|
/* Make bits 2,3 (initiator timeout) to 1,1
|
|
* for register CBUS_LINK_CONTROL_2
|
|
* REG_CBUS_LINK_CONTROL_2
|
|
*/
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0031);
|
|
regval = (regval | 0x0C);
|
|
/* REG_CBUS_LINK_CONTROL_2 */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0031, regval);
|
|
/* REG_MSC_TIMEOUT_LIMIT */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0022, 0x0F);
|
|
/* REG_CBUS_LINK_CONTROL_1 */
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0030, 0x01);
|
|
/* disallow vendor specific commands */
|
|
mhl_i2c_reg_modify(TX_PAGE_CBUS, 0x002E, BIT4, BIT4);
|
|
}
|
|
|
|
/*
|
|
* Configure the initial reg settings
|
|
*/
|
|
static void mhl_init_reg_settings(bool mhl_disc_en)
|
|
{
|
|
|
|
/*
|
|
* ============================================
|
|
* POWER UP
|
|
* ============================================
|
|
*/
|
|
|
|
/* Power up 1.2V core */
|
|
mhl_i2c_reg_write(TX_PAGE_L1, 0x003D, 0x3F);
|
|
/*
|
|
* Wait for the source power to be enabled
|
|
* before enabling pll clocks.
|
|
*/
|
|
msleep(50);
|
|
/* Enable Tx PLL Clock */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0011, 0x01);
|
|
/* Enable Tx Clock Path and Equalizer */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0012, 0x11);
|
|
/* Tx Source Termination ON */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0x10);
|
|
/* Enable 1X MHL Clock output */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0035, 0xAC);
|
|
/* Tx Differential Driver Config */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0031, 0x3C);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0033, 0xD9);
|
|
/* PLL Bandwidth Control */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0037, 0x02);
|
|
/*
|
|
* ============================================
|
|
* Analog PLL Control
|
|
* ============================================
|
|
*/
|
|
/* Enable Rx PLL clock */
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0080, 0x00);
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x00F8, 0x0C);
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0085, 0x02);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0000, 0x00);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0013, 0x60);
|
|
/* PLL Cal ref sel */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0017, 0x03);
|
|
/* VCO Cal */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x001A, 0x20);
|
|
/* Auto EQ */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0022, 0xE0);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0023, 0xC0);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0024, 0xA0);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0025, 0x80);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0026, 0x60);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0027, 0x40);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0028, 0x20);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0029, 0x00);
|
|
/* Rx PLL Bandwidth 4MHz */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0031, 0x0A);
|
|
/* Rx PLL Bandwidth value from I2C */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x0045, 0x06);
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x004B, 0x06);
|
|
/* Manual zone control */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x004C, 0xE0);
|
|
/* PLL Mode value */
|
|
mhl_i2c_reg_write(TX_PAGE_2, 0x004D, 0x00);
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0008, 0x35);
|
|
/*
|
|
* Discovery Control and Status regs
|
|
* Setting De-glitch time to 50 ms (default)
|
|
* Switch Control Disabled
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0011, 0xAD);
|
|
/* 1.8V CBUS VTH */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0014, 0x55);
|
|
/* RGND and single Discovery attempt */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0015, 0x11);
|
|
/* Ignore VBUS */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0017, 0x82);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0018, 0x24);
|
|
/* Pull-up resistance off for IDLE state */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0013, 0x8C);
|
|
/* Enable CBUS Discovery */
|
|
if (mhl_disc_en)
|
|
/* Enable MHL Discovery */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27);
|
|
else
|
|
/* Disable MHL Discovery */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x26);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0016, 0x20);
|
|
/* MHL CBUS Discovery - immediate comm. */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86);
|
|
/* Do not force HPD to 0 during wake-up from D3 */
|
|
if (mhl_msm_state->cur_state != POWER_STATE_D0_MHL)
|
|
mhl_drive_hpd(HPD_DOWN);
|
|
|
|
/* Enable Auto Soft RESET */
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0000, 0x084);
|
|
/* HDMI Transcode mode enable */
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x000D, 0x1C);
|
|
|
|
cbus_reset();
|
|
init_cbus_regs();
|
|
}
|
|
|
|
static int mhl_chip_init(void)
|
|
{
|
|
/* Read the chip rev ID */
|
|
mhl_msm_state->chip_rev_id = mhl_i2c_reg_read(TX_PAGE_L0, 0x04);
|
|
pr_debug("MHL: chip rev ID read=[%x]\n", mhl_msm_state->chip_rev_id);
|
|
|
|
/* Reset the TX chip */
|
|
mhl_sii_reset_pin(1);
|
|
msleep(20);
|
|
mhl_sii_reset_pin(0);
|
|
msleep(20);
|
|
mhl_sii_reset_pin(1);
|
|
/* MHL spec requires a 100 ms wait here. */
|
|
msleep(100);
|
|
|
|
/*
|
|
* Need to disable MHL discovery
|
|
*/
|
|
mhl_init_reg_settings(true);
|
|
|
|
/*
|
|
* Power down the chip to the
|
|
* D3 - a low power standby mode
|
|
* cable impedance measurement logic is operational
|
|
*/
|
|
switch_mode(POWER_STATE_D3);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* I2C probe
|
|
*/
|
|
static int mhl_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int ret;
|
|
struct msm_mhl_platform_data *tmp = client->dev.platform_data;
|
|
if (!tmp->mhl_enabled) {
|
|
ret = -ENODEV;
|
|
pr_warn("MHL feautre left disabled\n");
|
|
goto probe_early_exit;
|
|
}
|
|
mhl_msm_state->mhl_data = kzalloc(sizeof(struct msm_mhl_platform_data),
|
|
GFP_KERNEL);
|
|
if (!(mhl_msm_state->mhl_data)) {
|
|
ret = -ENOMEM;
|
|
pr_err("MHL I2C Probe failed - no mem\n");
|
|
goto probe_early_exit;
|
|
}
|
|
mhl_msm_state->i2c_client = client;
|
|
spin_lock_init(&mhl_state_lock);
|
|
i2c_set_clientdata(client, mhl_msm_state);
|
|
mhl_msm_state->mhl_data = client->dev.platform_data;
|
|
pr_debug("MHL: mhl_msm_state->mhl_data->irq=[%d]\n",
|
|
mhl_msm_state->mhl_data->irq);
|
|
msc_send_workqueue = create_workqueue("mhl_msc_cmd_queue");
|
|
|
|
mhl_msm_state->cur_state = POWER_STATE_D0_MHL;
|
|
/* Init GPIO stuff here */
|
|
ret = mhl_sii_gpio_setup(1);
|
|
if (ret) {
|
|
pr_err("MHL: mhl_gpio_init has failed\n");
|
|
ret = -ENODEV;
|
|
goto probe_early_exit;
|
|
}
|
|
mhl_sii_power_on();
|
|
/* MHL SII 8334 chip specific init */
|
|
mhl_chip_init();
|
|
init_completion(&mhl_msm_state->rgnd_done);
|
|
/* Request IRQ stuff here */
|
|
pr_debug("MHL: mhl_msm_state->mhl_data->irq=[%d]\n",
|
|
mhl_msm_state->mhl_data->irq);
|
|
ret = request_threaded_irq(mhl_msm_state->mhl_data->irq, NULL,
|
|
&mhl_tx_isr,
|
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
|
"mhl_tx_isr", mhl_msm_state);
|
|
if (ret) {
|
|
pr_err("request_threaded_irq failed, status: %d\n",
|
|
ret);
|
|
goto probe_exit;
|
|
} else {
|
|
pr_debug("request_threaded_irq succeeded\n");
|
|
}
|
|
|
|
INIT_WORK(&mhl_msm_state->mhl_msc_send_work, mhl_msc_send_work);
|
|
INIT_LIST_HEAD(&mhl_msm_state->list_cmd);
|
|
mhl_msm_state->msc_command_put_work = list_cmd_put;
|
|
mhl_msm_state->msc_command_get_work = list_cmd_get;
|
|
init_completion(&mhl_msm_state->msc_cmd_done);
|
|
|
|
pr_debug("i2c probe successful\n");
|
|
return 0;
|
|
|
|
probe_exit:
|
|
if (mhl_msm_state->mhl_data) {
|
|
/* free the gpios */
|
|
mhl_sii_gpio_setup(0);
|
|
kfree(mhl_msm_state->mhl_data);
|
|
mhl_msm_state->mhl_data = NULL;
|
|
}
|
|
probe_early_exit:
|
|
return ret;
|
|
}
|
|
|
|
static int mhl_i2c_remove(struct i2c_client *client)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
mhl_sii_gpio_setup(0);
|
|
kfree(mhl_msm_state->mhl_data);
|
|
return 0;
|
|
}
|
|
|
|
static void list_cmd_put(struct msc_command_struct *cmd)
|
|
{
|
|
struct msc_cmd_envelope *new_cmd;
|
|
new_cmd = vmalloc(sizeof(struct msc_cmd_envelope));
|
|
memcpy(&new_cmd->msc_cmd_msg, cmd,
|
|
sizeof(struct msc_command_struct));
|
|
/* Need to check for queue getting filled up */
|
|
list_add_tail(&new_cmd->msc_queue_envelope, &mhl_msm_state->list_cmd);
|
|
}
|
|
|
|
static struct msc_command_struct *list_cmd_get(void)
|
|
{
|
|
struct msc_cmd_envelope *cmd_env =
|
|
list_first_entry(&mhl_msm_state->list_cmd,
|
|
struct msc_cmd_envelope, msc_queue_envelope);
|
|
list_del(&cmd_env->msc_queue_envelope);
|
|
return &cmd_env->msc_cmd_msg;
|
|
}
|
|
|
|
static void mhl_msc_send_work(struct work_struct *work)
|
|
{
|
|
int ret;
|
|
/*
|
|
* Remove item from the queue
|
|
* and schedule it
|
|
*/
|
|
struct msc_command_struct *req;
|
|
while (!list_empty(&mhl_msm_state->list_cmd)) {
|
|
req = mhl_msm_state->msc_command_get_work();
|
|
ret = mhl_send_msc_command(req);
|
|
if (ret == -EAGAIN)
|
|
pr_err("MHL: Queue still busy!!\n");
|
|
else {
|
|
vfree(req);
|
|
pr_debug("MESSAGE SENT!!!!\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int __init mhl_msm_init(void)
|
|
{
|
|
int32_t ret;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
mhl_msm_state = kzalloc(sizeof(struct mhl_msm_state_t), GFP_KERNEL);
|
|
if (!mhl_msm_state) {
|
|
pr_err("mhl_msm_init FAILED: out of memory\n");
|
|
ret = -ENOMEM;
|
|
goto init_exit;
|
|
}
|
|
mhl_msm_state->i2c_client = NULL;
|
|
ret = i2c_add_driver(&mhl_sii_i2c_driver);
|
|
if (ret) {
|
|
pr_err("MHL: I2C driver add failed: %d\n", ret);
|
|
ret = -ENODEV;
|
|
goto init_exit;
|
|
} else {
|
|
if (mhl_msm_state->i2c_client == NULL) {
|
|
i2c_del_driver(&mhl_sii_i2c_driver);
|
|
pr_err("MHL: I2C driver add failed\n");
|
|
ret = -ENODEV;
|
|
goto init_exit;
|
|
}
|
|
pr_info("MHL: I2C driver added\n");
|
|
}
|
|
|
|
return 0;
|
|
init_exit:
|
|
pr_err("Exiting from the init with err\n");
|
|
if (!mhl_msm_state) {
|
|
kfree(mhl_msm_state);
|
|
mhl_msm_state = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void mhl_msc_sched_work(struct msc_command_struct *req)
|
|
{
|
|
/*
|
|
* Put an item to the queue
|
|
* and schedule work
|
|
*/
|
|
mhl_msm_state->msc_command_put_work(req);
|
|
queue_work(msc_send_workqueue, &mhl_msm_state->mhl_msc_send_work);
|
|
}
|
|
|
|
static void switch_mode(enum mhl_st_type to_mode)
|
|
{
|
|
unsigned long flags;
|
|
|
|
switch (to_mode) {
|
|
case POWER_STATE_D0_NO_MHL:
|
|
break;
|
|
case POWER_STATE_D0_MHL:
|
|
mhl_init_reg_settings(true);
|
|
/* REG_DISC_CTRL1 */
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT1 | BIT0, BIT0);
|
|
|
|
/*
|
|
* TPI_DEVICE_POWER_STATE_CTRL_REG
|
|
* TX_POWER_STATE_MASK = BIT1 | BIT0
|
|
*/
|
|
mhl_i2c_reg_modify(TX_PAGE_TPI, 0x001E, BIT1 | BIT0, 0x00);
|
|
break;
|
|
case POWER_STATE_D3:
|
|
if (mhl_msm_state->cur_state != POWER_STATE_D3) {
|
|
/* Force HPD to 0 when not in MHL mode. */
|
|
mhl_drive_hpd(HPD_DOWN);
|
|
/*
|
|
* Change TMDS termination to high impedance
|
|
* on disconnection.
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0xD0);
|
|
msleep(50);
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0010,
|
|
BIT1 | BIT0, 0x00);
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x003D, BIT0, 0x00);
|
|
spin_lock_irqsave(&mhl_state_lock, flags);
|
|
mhl_msm_state->cur_state = POWER_STATE_D3;
|
|
spin_unlock_irqrestore(&mhl_state_lock, flags);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mhl_drive_hpd(uint8_t to_state)
|
|
{
|
|
if (mhl_msm_state->cur_state != POWER_STATE_D0_MHL) {
|
|
pr_err("MHL: invalid state to ctrl HPD\n");
|
|
return;
|
|
}
|
|
|
|
pr_debug("%s: To state=[0x%x]\n", __func__, to_state);
|
|
if (to_state == HPD_UP) {
|
|
/*
|
|
* Drive HPD to UP state
|
|
*
|
|
* The below two reg configs combined
|
|
* enable TMDS output.
|
|
*/
|
|
|
|
/* Enable TMDS on TMDS_CCTRL */
|
|
mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, BIT4);
|
|
|
|
/*
|
|
* Set HPD_OUT_OVR_EN = HPD State
|
|
* EDID read and Un-force HPD (from low)
|
|
* propogate to src let HPD float by clearing
|
|
* HPD OUT OVRRD EN
|
|
*/
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, BIT4, 0x00);
|
|
} else {
|
|
/*
|
|
* Drive HPD to DOWN state
|
|
* Disable TMDS Output on REG_TMDS_CCTRL
|
|
* Enable/Disable TMDS output (MHL TMDS output only)
|
|
*/
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x20, BIT4 | BIT5, BIT4);
|
|
mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, 0x00);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void mhl_msm_connection(void)
|
|
{
|
|
uint8_t val;
|
|
unsigned long flags;
|
|
|
|
pr_debug("%s: cur state = [0x%x]\n", __func__,
|
|
mhl_msm_state->cur_state);
|
|
|
|
if (mhl_msm_state->cur_state == POWER_STATE_D0_MHL) {
|
|
/* Already in D0 - MHL power state */
|
|
return;
|
|
}
|
|
spin_lock_irqsave(&mhl_state_lock, flags);
|
|
mhl_msm_state->cur_state = POWER_STATE_D0_MHL;
|
|
spin_unlock_irqrestore(&mhl_state_lock, flags);
|
|
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0x10);
|
|
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x07, 0xF2);
|
|
|
|
/*
|
|
* Keep the discovery enabled. Need RGND interrupt
|
|
* Possibly chip disables discovery after MHL_EST??
|
|
* Need to re-enable here
|
|
*/
|
|
val = mhl_i2c_reg_read(TX_PAGE_3, 0x10);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x10, val | BIT0);
|
|
|
|
return;
|
|
}
|
|
|
|
static void mhl_msm_disconnection(void)
|
|
{
|
|
/*
|
|
* MHL TX CTL1
|
|
* Disabling Tx termination
|
|
*/
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0xD0);
|
|
/* Change HPD line to drive it low */
|
|
mhl_drive_hpd(HPD_DOWN);
|
|
/* switch power state to D3 */
|
|
switch_mode(POWER_STATE_D3);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If hardware detected a change in impedance and raised an INTR
|
|
* We check the range of this impedance to infer if the connected
|
|
* device is MHL or USB and take appropriate actions.
|
|
*/
|
|
static int mhl_msm_read_rgnd_int(void)
|
|
{
|
|
uint8_t rgnd_imp;
|
|
|
|
/*
|
|
* DISC STATUS REG 2
|
|
* 1:0 RGND
|
|
* 00 - open (USB)
|
|
* 01 - 2 kOHM (USB)
|
|
* 10 - 1 kOHM ***(MHL)**** It's range 800 - 1200 OHM from MHL spec
|
|
* 11 - short (USB)
|
|
*/
|
|
rgnd_imp = (mhl_i2c_reg_read(TX_PAGE_3, 0x001C) & (BIT1 | BIT0));
|
|
pr_debug("Imp Range read = %02X\n", (int)rgnd_imp);
|
|
|
|
if (0x02 == rgnd_imp) {
|
|
pr_debug("MHL: MHL DEVICE!!!\n");
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT0, BIT0);
|
|
mhl_msm_state->mhl_mode = TRUE;
|
|
if (notify_usb_online)
|
|
notify_usb_online(1);
|
|
} else {
|
|
pr_debug("MHL: NON-MHL DEVICE!!!\n");
|
|
mhl_msm_state->mhl_mode = FALSE;
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT3, BIT3);
|
|
switch_mode(POWER_STATE_D3);
|
|
}
|
|
complete(&mhl_msm_state->rgnd_done);
|
|
return mhl_msm_state->mhl_mode ?
|
|
MHL_DISCOVERY_RESULT_MHL : MHL_DISCOVERY_RESULT_USB;
|
|
}
|
|
|
|
static void force_usb_switch_open(void)
|
|
{
|
|
/*DISABLE_DISCOVERY*/
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT0, 0);
|
|
/* Force USB ID switch to open*/
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0015, BIT6, BIT6);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86);
|
|
/* Force HPD to 0 when not in Mobile HD mode. */
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0020, BIT5 | BIT4, BIT4);
|
|
}
|
|
|
|
static void release_usb_switch_open(void)
|
|
{
|
|
msleep(50);
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0015, BIT6, 0x00);
|
|
mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT0, BIT0);
|
|
}
|
|
|
|
static void int_4_isr(void)
|
|
{
|
|
uint8_t status, reg ;
|
|
|
|
/* INTR_STATUS4 */
|
|
status = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
|
|
|
|
/*
|
|
* When I2C is inoperational (D3) and
|
|
* a previous interrupt brought us here,
|
|
* do nothing.
|
|
*/
|
|
if ((0x00 == status) && (mhl_msm_state->cur_state == POWER_STATE_D3)) {
|
|
pr_debug("MHL: spurious interrupt\n");
|
|
return;
|
|
}
|
|
if (0xFF != status) {
|
|
if ((status & BIT0) && (mhl_msm_state->chip_rev_id < 1)) {
|
|
uint8_t tmds_cstat;
|
|
uint8_t mhl_fifo_status;
|
|
|
|
/* TMDS CSTAT */
|
|
tmds_cstat = mhl_i2c_reg_read(TX_PAGE_3, 0x0040);
|
|
|
|
pr_debug("TMDS CSTAT: 0x%02x\n", tmds_cstat);
|
|
|
|
if (tmds_cstat & 0x02) {
|
|
mhl_fifo_status = mhl_i2c_reg_read(TX_PAGE_3,
|
|
0x0023);
|
|
pr_debug("MHL FIFO status: 0x%02x\n",
|
|
mhl_fifo_status);
|
|
if (mhl_fifo_status & 0x0C) {
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0023,
|
|
0x0C);
|
|
|
|
pr_debug("Apply MHL FIFO Reset\n");
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0000,
|
|
0x94);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0000,
|
|
0x84);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status & BIT1)
|
|
pr_debug("MHL: INT4 BIT1 is set\n");
|
|
|
|
/* MHL_EST interrupt */
|
|
if (status & BIT2) {
|
|
pr_debug("mhl_msm_connection() from ISR\n");
|
|
mhl_connect_api(true);
|
|
mhl_msm_connection();
|
|
pr_debug("MHL Connect Drv: INT4 Status = %02X\n",
|
|
(int) status);
|
|
} else if (status & BIT3) {
|
|
pr_debug("MHL: uUSB-A type device detected.\n");
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x001C, 0x80);
|
|
switch_mode(POWER_STATE_D3);
|
|
}
|
|
|
|
if (status & BIT5) {
|
|
mhl_connect_api(false);
|
|
/* Clear interrupts - REG INTR4 */
|
|
reg = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0021, reg);
|
|
mhl_msm_disconnection();
|
|
if (notify_usb_online)
|
|
notify_usb_online(0);
|
|
pr_debug("MHL Disconnect Drv: INT4 Status = %02X\n",
|
|
(int)status);
|
|
}
|
|
|
|
if ((mhl_msm_state->cur_state != POWER_STATE_D0_MHL) &&\
|
|
(status & BIT6)) {
|
|
/* RGND READY Intr */
|
|
switch_mode(POWER_STATE_D0_MHL);
|
|
mhl_msm_read_rgnd_int();
|
|
}
|
|
|
|
/* Can't succeed at these in D3 */
|
|
if (mhl_msm_state->cur_state != POWER_STATE_D3) {
|
|
/* CBUS Lockout interrupt? */
|
|
/*
|
|
* Hardware detection mechanism figures that
|
|
* CBUS line is latched and raises this intr
|
|
* where we force usb switch open and release
|
|
*/
|
|
if (status & BIT4) {
|
|
force_usb_switch_open();
|
|
release_usb_switch_open();
|
|
}
|
|
}
|
|
}
|
|
pr_debug("MHL END Drv: INT4 Status = %02X\n", (int) status);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0021, status);
|
|
|
|
return;
|
|
}
|
|
|
|
static void int_5_isr(void)
|
|
{
|
|
uint8_t intr_5_stat;
|
|
|
|
/*
|
|
* Clear INT 5
|
|
* INTR5 is related to FIFO underflow/overflow reset
|
|
* which is handled in 8334 by auto FIFO reset
|
|
*/
|
|
intr_5_stat = mhl_i2c_reg_read(TX_PAGE_3, 0x0023);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0023, intr_5_stat);
|
|
}
|
|
|
|
|
|
static void int_1_isr(void)
|
|
{
|
|
/* This ISR mainly handles the HPD status changes */
|
|
uint8_t intr_1_stat;
|
|
uint8_t cbus_stat;
|
|
|
|
/* INTR STATUS 1 */
|
|
intr_1_stat = mhl_i2c_reg_read(TX_PAGE_L0, 0x0071);
|
|
|
|
if (intr_1_stat) {
|
|
/* Clear interrupts */
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0071, intr_1_stat);
|
|
if (BIT6 & intr_1_stat) {
|
|
/*
|
|
* HPD status change event is pending
|
|
* Read CBUS HPD status for this info
|
|
*/
|
|
|
|
/* MSC REQ ABRT REASON */
|
|
cbus_stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0D);
|
|
if (BIT6 & cbus_stat)
|
|
mhl_drive_hpd(HPD_UP);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void mhl_cbus_process_errors(u8 int_status)
|
|
{
|
|
u8 abort_reason = 0;
|
|
if (int_status & BIT2) {
|
|
abort_reason = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0B);
|
|
pr_debug("%s: CBUS DDC Abort Reason(0x%02x)\n",
|
|
__func__, abort_reason);
|
|
}
|
|
if (int_status & BIT5) {
|
|
abort_reason = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0D);
|
|
pr_debug("%s: CBUS MSC Requestor Abort Reason(0x%02x)\n",
|
|
__func__, abort_reason);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0D, 0xFF);
|
|
}
|
|
if (int_status & BIT6) {
|
|
abort_reason = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0E);
|
|
pr_debug("%s: CBUS MSC Responder Abort Reason(0x%02x)\n",
|
|
__func__, abort_reason);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0E, 0xFF);
|
|
}
|
|
}
|
|
|
|
static int mhl_msc_command_done(struct msc_command_struct *req)
|
|
{
|
|
switch (req->command) {
|
|
case MHL_WRITE_STAT:
|
|
if (req->offset == MHL_STATUS_REG_LINK_MODE) {
|
|
if (req->payload.data[0]
|
|
& MHL_STATUS_PATH_ENABLED) {
|
|
/* Enable TMDS output */
|
|
mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080,
|
|
BIT4, BIT4);
|
|
} else
|
|
/* Disable TMDS output */
|
|
mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080,
|
|
BIT4, BIT4);
|
|
}
|
|
break;
|
|
case MHL_READ_DEVCAP:
|
|
mhl_msm_state->devcap_state |= BIT(req->offset);
|
|
switch (req->offset) {
|
|
case MHL_DEV_CATEGORY_OFFSET:
|
|
if (req->retval & MHL_DEV_CATEGORY_POW_BIT) {
|
|
/*
|
|
* Enable charging
|
|
*/
|
|
} else {
|
|
/*
|
|
* Disable charging
|
|
*/
|
|
}
|
|
break;
|
|
case DEVCAP_OFFSET_MHL_VERSION:
|
|
case DEVCAP_OFFSET_INT_STAT_SIZE:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_send_msc_command(struct msc_command_struct *req)
|
|
{
|
|
int timeout;
|
|
u8 start_bit = 0x00;
|
|
u8 *burst_data;
|
|
int i;
|
|
|
|
if (mhl_msm_state->cur_state != POWER_STATE_D0_MHL) {
|
|
pr_debug("%s: power_state:%02x CBUS(0x0A):%02x\n",
|
|
__func__,
|
|
mhl_msm_state->cur_state, mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0A));
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (!req)
|
|
return -EFAULT;
|
|
|
|
pr_debug("%s: command=0x%02x offset=0x%02x %02x %02x",
|
|
__func__,
|
|
req->command,
|
|
req->offset,
|
|
req->payload.data[0],
|
|
req->payload.data[1]);
|
|
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x13, req->offset);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x14, req->payload.data[0]);
|
|
|
|
switch (req->command) {
|
|
case MHL_SET_INT:
|
|
case MHL_WRITE_STAT:
|
|
start_bit = MSC_START_BIT_WRITE_REG;
|
|
break;
|
|
case MHL_READ_DEVCAP:
|
|
start_bit = MSC_START_BIT_READ_REG;
|
|
break;
|
|
case MHL_GET_STATE:
|
|
case MHL_GET_VENDOR_ID:
|
|
case MHL_SET_HPD:
|
|
case MHL_CLR_HPD:
|
|
case MHL_GET_SC1_ERRORCODE:
|
|
case MHL_GET_DDC_ERRORCODE:
|
|
case MHL_GET_MSC_ERRORCODE:
|
|
case MHL_GET_SC3_ERRORCODE:
|
|
start_bit = MSC_START_BIT_MSC_CMD;
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x13, req->command);
|
|
break;
|
|
case MHL_MSC_MSG:
|
|
start_bit = MSC_START_BIT_VS_CMD;
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x15, req->payload.data[1]);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x13, req->command);
|
|
break;
|
|
case MHL_WRITE_BURST:
|
|
start_bit = MSC_START_BIT_WRITE_BURST;
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x20, req->length - 1);
|
|
if (!(req->payload.burst_data)) {
|
|
pr_err("%s: burst data is null!\n", __func__);
|
|
goto cbus_send_fail;
|
|
}
|
|
burst_data = req->payload.burst_data;
|
|
for (i = 0; i < req->length; i++, burst_data++)
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xC0 + i, *burst_data);
|
|
break;
|
|
default:
|
|
pr_err("%s: unknown command! (%02x)\n",
|
|
__func__, req->command);
|
|
goto cbus_send_fail;
|
|
}
|
|
|
|
INIT_COMPLETION(mhl_msm_state->msc_cmd_done);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x12, start_bit);
|
|
timeout = wait_for_completion_interruptible_timeout
|
|
(&mhl_msm_state->msc_cmd_done, HZ);
|
|
if (!timeout) {
|
|
pr_err("%s: cbus_command_send timed out!\n", __func__);
|
|
goto cbus_send_fail;
|
|
}
|
|
|
|
switch (req->command) {
|
|
case MHL_READ_DEVCAP:
|
|
/* devcap */
|
|
req->retval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x16);
|
|
pr_debug("Read CBUS[0x16]=[%02x]\n", req->retval);
|
|
break;
|
|
case MHL_MSC_MSG:
|
|
/* check if MSC_MSG NACKed */
|
|
if (mhl_i2c_reg_read(TX_PAGE_CBUS, 0x20) & BIT6)
|
|
return -EAGAIN;
|
|
default:
|
|
req->retval = 0;
|
|
break;
|
|
}
|
|
mhl_msc_command_done(req);
|
|
pr_debug("%s: msc cmd done\n", __func__);
|
|
return 0;
|
|
|
|
cbus_send_fail:
|
|
return -EFAULT;
|
|
}
|
|
|
|
static int mhl_msc_send_set_int(u8 offset, u8 mask)
|
|
{
|
|
struct msc_command_struct req;
|
|
req.command = MHL_SET_INT;
|
|
req.offset = offset;
|
|
req.payload.data[0] = mask;
|
|
mhl_msc_sched_work(&req);
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_msc_send_write_stat(u8 offset, u8 value)
|
|
{
|
|
struct msc_command_struct req;
|
|
req.command = MHL_WRITE_STAT;
|
|
req.offset = offset;
|
|
req.payload.data[0] = value;
|
|
mhl_msc_sched_work(&req);
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_msc_send_msc_msg(u8 sub_cmd, u8 cmd_data)
|
|
{
|
|
struct msc_command_struct req;
|
|
req.command = MHL_MSC_MSG;
|
|
req.payload.data[0] = sub_cmd;
|
|
req.payload.data[1] = cmd_data;
|
|
mhl_msc_sched_work(&req);
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_msc_read_devcap(u8 offset)
|
|
{
|
|
struct msc_command_struct req;
|
|
if (offset < 0 || offset > 15)
|
|
return -EFAULT;
|
|
req.command = MHL_READ_DEVCAP;
|
|
req.offset = offset;
|
|
req.payload.data[0] = 0;
|
|
mhl_msc_sched_work(&req);
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_msc_read_devcap_all(void)
|
|
{
|
|
int offset;
|
|
int ret;
|
|
|
|
for (offset = 0; offset < DEVCAP_SIZE; offset++) {
|
|
ret = mhl_msc_read_devcap(offset);
|
|
msleep(200);
|
|
if (ret == -EFAULT) {
|
|
pr_err("%s: queue busy!\n", __func__);
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* supported RCP key code */
|
|
static const u8 rcp_key_code_tbl[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00~0x07 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x08~0x0f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x10~0x17 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x18~0x1f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x20~0x27 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x28~0x2f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x30~0x37 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x38~0x3f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40~0x47 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48~0x4f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50~0x57 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58~0x5f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60~0x67 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68~0x6f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70~0x77 */
|
|
0, 0, 0, 0, 0, 0, 0, 0 /* 0x78~0x7f */
|
|
};
|
|
|
|
static int mhl_rcp_recv(u8 key_code)
|
|
{
|
|
int rc;
|
|
if (rcp_key_code_tbl[(key_code & 0x7f)]) {
|
|
/*
|
|
* TODO: Take action for the RCP cmd
|
|
*/
|
|
|
|
/* send ack to rcp cmd*/
|
|
rc = mhl_msc_send_msc_msg(
|
|
MHL_MSC_MSG_RCPK,
|
|
key_code);
|
|
} else {
|
|
/* send rcp error */
|
|
rc = mhl_msc_send_msc_msg(
|
|
MHL_MSC_MSG_RCPE,
|
|
MHL_RCPE_UNSUPPORTED_KEY_CODE);
|
|
if (rc)
|
|
return rc;
|
|
/* send rcpk after rcpe send */
|
|
rc = mhl_msc_send_msc_msg(
|
|
MHL_MSC_MSG_RCPK,
|
|
key_code);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int mhl_rap_action(u8 action_code)
|
|
{
|
|
switch (action_code) {
|
|
case MHL_RAP_CONTENT_ON:
|
|
/*
|
|
* Enable TMDS on TMDS_CCTRL
|
|
*/
|
|
mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, BIT4);
|
|
break;
|
|
case MHL_RAP_CONTENT_OFF:
|
|
/*
|
|
* Disable TMDS on TMDS_CCTRL
|
|
*/
|
|
mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, 0x00);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_rap_recv(u8 action_code)
|
|
{
|
|
u8 error_code;
|
|
|
|
switch (action_code) {
|
|
/*case MHL_RAP_POLL:*/
|
|
case MHL_RAP_CONTENT_ON:
|
|
case MHL_RAP_CONTENT_OFF:
|
|
mhl_rap_action(action_code);
|
|
error_code = MHL_RAPK_NO_ERROR;
|
|
/* notify userspace */
|
|
break;
|
|
default:
|
|
error_code = MHL_RAPK_UNRECOGNIZED_ACTION_CODE;
|
|
break;
|
|
}
|
|
/* prior send rapk */
|
|
return mhl_msc_send_msc_msg(
|
|
MHL_MSC_MSG_RAPK,
|
|
error_code);
|
|
}
|
|
|
|
static int mhl_msc_recv_msc_msg(u8 sub_cmd, u8 cmd_data)
|
|
{
|
|
int rc = 0;
|
|
switch (sub_cmd) {
|
|
case MHL_MSC_MSG_RCP:
|
|
pr_debug("MHL: receive RCP(0x%02x)\n", cmd_data);
|
|
rc = mhl_rcp_recv(cmd_data);
|
|
break;
|
|
case MHL_MSC_MSG_RCPK:
|
|
pr_debug("MHL: receive RCPK(0x%02x)\n", cmd_data);
|
|
break;
|
|
case MHL_MSC_MSG_RCPE:
|
|
pr_debug("MHL: receive RCPE(0x%02x)\n", cmd_data);
|
|
break;
|
|
case MHL_MSC_MSG_RAP:
|
|
pr_debug("MHL: receive RAP(0x%02x)\n", cmd_data);
|
|
rc = mhl_rap_recv(cmd_data);
|
|
break;
|
|
case MHL_MSC_MSG_RAPK:
|
|
pr_debug("MHL: receive RAPK(0x%02x)\n", cmd_data);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int mhl_msc_recv_set_int(u8 offset, u8 set_int)
|
|
{
|
|
if (offset >= 2)
|
|
return -EFAULT;
|
|
|
|
switch (offset) {
|
|
case 0:
|
|
/* DCAP_CHG */
|
|
if (set_int & MHL_INT_DCAP_CHG) {
|
|
/* peer dcap has changed */
|
|
if (mhl_msc_read_devcap_all() == -EBUSY) {
|
|
pr_err("READ DEVCAP FAILED to send successfully\n");
|
|
break;
|
|
}
|
|
}
|
|
/* DSCR_CHG */
|
|
if (set_int & MHL_INT_DSCR_CHG)
|
|
;
|
|
/* REQ_WRT */
|
|
if (set_int & MHL_INT_REQ_WRT) {
|
|
/* SET_INT: GRT_WRT */
|
|
mhl_msc_send_set_int(
|
|
MHL_RCHANGE_INT,
|
|
MHL_INT_GRT_WRT);
|
|
}
|
|
/* GRT_WRT */
|
|
if (set_int & MHL_INT_GRT_WRT)
|
|
;
|
|
break;
|
|
case 1:
|
|
/* EDID_CHG */
|
|
if (set_int & MHL_INT_EDID_CHG) {
|
|
/* peer EDID has changed.
|
|
* toggle HPD to read EDID again
|
|
* In 8x30 FLUID HDMI HPD line
|
|
* is not connected
|
|
* with MHL 8334 transmitter
|
|
*/
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mhl_msc_recv_write_stat(u8 offset, u8 value)
|
|
{
|
|
if (offset >= 2)
|
|
return -EFAULT;
|
|
|
|
switch (offset) {
|
|
case 0:
|
|
/* DCAP_RDY */
|
|
/*
|
|
* Connected Device bits changed and DEVCAP READY
|
|
*/
|
|
pr_debug("MHL: value [0x%02x]\n", value);
|
|
pr_debug("MHL: offset [0x%02x]\n", offset);
|
|
pr_debug("MHL: devcap state [0x%02x]\n",
|
|
mhl_msm_state->devcap_state);
|
|
pr_debug("MHL: MHL_STATUS_DCAP_RDY [0x%02x]\n",
|
|
MHL_STATUS_DCAP_RDY);
|
|
if (((value ^ mhl_msm_state->devcap_state) &
|
|
MHL_STATUS_DCAP_RDY)) {
|
|
if (value & MHL_STATUS_DCAP_RDY) {
|
|
if (mhl_msc_read_devcap_all() == -EBUSY) {
|
|
pr_err("READ DEVCAP FAILED to send successfully\n");
|
|
break;
|
|
}
|
|
} else {
|
|
/* peer dcap turned not ready */
|
|
/*
|
|
* Clear DEVCAP READY state
|
|
*/
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
/* PATH_EN */
|
|
/*
|
|
* Connected Device bits changed and PATH ENABLED
|
|
*/
|
|
if ((value ^ mhl_msm_state->path_en_state)
|
|
& MHL_STATUS_PATH_ENABLED) {
|
|
if (value & MHL_STATUS_PATH_ENABLED) {
|
|
mhl_msm_state->path_en_state
|
|
|= (MHL_STATUS_PATH_ENABLED |
|
|
MHL_STATUS_CLK_MODE_NORMAL);
|
|
mhl_msc_send_write_stat(
|
|
MHL_STATUS_REG_LINK_MODE,
|
|
mhl_msm_state->path_en_state);
|
|
} else {
|
|
mhl_msm_state->path_en_state
|
|
&= ~(MHL_STATUS_PATH_ENABLED |
|
|
MHL_STATUS_CLK_MODE_NORMAL);
|
|
mhl_msc_send_write_stat(
|
|
MHL_STATUS_REG_LINK_MODE,
|
|
mhl_msm_state->path_en_state);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
mhl_msm_state->path_en_state = value;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* MSC, RCP, RAP messages - mandatory for compliance */
|
|
static void mhl_cbus_isr(void)
|
|
{
|
|
uint8_t regval;
|
|
int req_done = FALSE;
|
|
uint8_t sub_cmd = 0x0;
|
|
uint8_t cmd_data = 0x0;
|
|
int msc_msg_recved = FALSE;
|
|
int rc = -1;
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x08);
|
|
if (regval == 0xff)
|
|
return;
|
|
|
|
/*
|
|
* clear all interrupts that were raised
|
|
* even if we did not process
|
|
*/
|
|
if (regval)
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x08, regval);
|
|
|
|
pr_debug("%s: CBUS_INT = %02x\n", __func__, regval);
|
|
|
|
/* MSC_MSG (RCP/RAP) */
|
|
if (regval & BIT3) {
|
|
sub_cmd = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x18);
|
|
cmd_data = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x19);
|
|
msc_msg_recved = TRUE;
|
|
}
|
|
/* MSC_MT_ABRT/MSC_MR_ABRT/DDC_ABORT */
|
|
if (regval & (BIT6 | BIT5 | BIT2))
|
|
mhl_cbus_process_errors(regval);
|
|
|
|
/* MSC_REQ_DONE */
|
|
if (regval & BIT4)
|
|
req_done = TRUE;
|
|
|
|
/* Now look for interrupts on CBUS_MSC_INT2 */
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x1E);
|
|
|
|
/* clear all interrupts that were raised */
|
|
/* even if we did not process */
|
|
if (regval)
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x1E, regval);
|
|
|
|
pr_debug("%s: CBUS_MSC_INT2 = %02x\n", __func__, regval);
|
|
|
|
/* received SET_INT */
|
|
if (regval & BIT2) {
|
|
uint8_t intr;
|
|
intr = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xA0);
|
|
mhl_msc_recv_set_int(0, intr);
|
|
|
|
pr_debug("%s: MHL_INT_0 = %02x\n", __func__, intr);
|
|
intr = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xA1);
|
|
mhl_msc_recv_set_int(1, intr);
|
|
|
|
pr_debug("%s: MHL_INT_1 = %02x\n", __func__, intr);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA0, 0xFF);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA1, 0xFF);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA2, 0xFF);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xA3, 0xFF);
|
|
}
|
|
|
|
/* received WRITE_STAT */
|
|
if (regval & BIT3) {
|
|
uint8_t stat;
|
|
stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xB0);
|
|
mhl_msc_recv_write_stat(0, stat);
|
|
|
|
pr_debug("%s: MHL_STATUS_0 = %02x\n", __func__, stat);
|
|
stat = mhl_i2c_reg_read(TX_PAGE_CBUS, 0xB1);
|
|
mhl_msc_recv_write_stat(1, stat);
|
|
pr_debug("%s: MHL_STATUS_1 = %02x\n", __func__, stat);
|
|
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB0, 0xFF);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB1, 0xFF);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB2, 0xFF);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0xB3, 0xFF);
|
|
}
|
|
|
|
/* received MSC_MSG */
|
|
if (msc_msg_recved) {
|
|
/*mhl msc recv msc msg*/
|
|
rc = mhl_msc_recv_msc_msg(sub_cmd, cmd_data);
|
|
if (rc)
|
|
pr_err("MHL: mhl msc recv msc msg failed(%d)!\n", rc);
|
|
}
|
|
/* complete last command */
|
|
if (req_done)
|
|
complete_all(&mhl_msm_state->msc_cmd_done);
|
|
|
|
return;
|
|
}
|
|
|
|
static void clear_all_intrs(void)
|
|
{
|
|
uint8_t regval = 0x00;
|
|
/*
|
|
* intr status debug
|
|
*/
|
|
pr_debug("********* EXITING ISR MASK CHECK ?? *************\n");
|
|
pr_debug("Drv: INT1 MASK = %02X\n",
|
|
(int) mhl_i2c_reg_read(TX_PAGE_L0, 0x0071));
|
|
pr_debug("Drv: INT3 MASK = %02X\n",
|
|
(int) mhl_i2c_reg_read(TX_PAGE_L0, 0x0077));
|
|
pr_debug("Drv: INT4 MASK = %02X\n",
|
|
(int) mhl_i2c_reg_read(TX_PAGE_3, 0x0021));
|
|
pr_debug("Drv: INT5 MASK = %02X\n",
|
|
(int) mhl_i2c_reg_read(TX_PAGE_3, 0x0023));
|
|
pr_debug("Drv: CBUS1 MASK = %02X\n",
|
|
(int) mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0009));
|
|
pr_debug("Drv: CBUS2 MASK = %02X\n",
|
|
(int) mhl_i2c_reg_read(TX_PAGE_CBUS, 0x001F));
|
|
pr_debug("********* END OF ISR MASK CHECK *************\n");
|
|
|
|
pr_debug("********* EXITING IN ISR ?? *************\n");
|
|
regval = mhl_i2c_reg_read(TX_PAGE_L0, 0x0071);
|
|
pr_debug("Drv: INT1 Status = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0071, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_L0, 0x0072);
|
|
pr_debug("Drv: INT2 Status = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0072, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_L0, 0x0073);
|
|
pr_debug("Drv: INT3 Status = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_L0, 0x0073, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
|
|
pr_debug("Drv: INT4 Status = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0021, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_3, 0x0023);
|
|
pr_debug("Drv: INT5 Status = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_3, 0x0023, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0008);
|
|
pr_debug("Drv: cbusInt Status = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0008, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x001E);
|
|
pr_debug("Drv: CBUS INTR_2: %d\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x001E, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A0);
|
|
pr_debug("Drv: A0 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A0, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A1);
|
|
pr_debug("Drv: A1 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A1, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A2);
|
|
pr_debug("Drv: A2 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A2, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A3);
|
|
pr_debug("Drv: A3 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A3, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B0);
|
|
pr_debug("Drv: B0 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B0, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B1);
|
|
pr_debug("Drv: B1 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B1, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B2);
|
|
pr_debug("Drv: B2 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B2, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B3);
|
|
pr_debug("Drv: B3 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B3, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E0);
|
|
pr_debug("Drv: E0 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E0, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E1);
|
|
pr_debug("Drv: E1 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E1, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E2);
|
|
pr_debug("Drv: E2 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E2, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E3);
|
|
pr_debug("Drv: E3 STATUS Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E3, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F0);
|
|
pr_debug("Drv: F0 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F0, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F1);
|
|
pr_debug("Drv: F1 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F1, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F2);
|
|
pr_debug("Drv: F2 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F2, regval);
|
|
|
|
regval = mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F3);
|
|
pr_debug("Drv: F3 INT Set = %02X\n", (int)regval);
|
|
mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F3, regval);
|
|
pr_debug("********* END OF EXITING IN ISR *************\n");
|
|
}
|
|
|
|
static irqreturn_t mhl_tx_isr(int irq, void *dev_id)
|
|
{
|
|
/*
|
|
* Check RGND, MHL_EST, CBUS_LOCKOUT, SCDT
|
|
* interrupts. In D3, we get only RGND
|
|
*/
|
|
int_4_isr();
|
|
|
|
pr_debug("MHL: Current POWER state is [0x%x]\n",
|
|
mhl_msm_state->cur_state);
|
|
if (mhl_msm_state->cur_state == POWER_STATE_D0_MHL) {
|
|
/*
|
|
* If int_4_isr() didn't move the tx to D3
|
|
* on disconnect, continue to check other
|
|
* interrupt sources.
|
|
*/
|
|
int_5_isr();
|
|
|
|
/*
|
|
* Check for any peer messages for DCAP_CHG etc
|
|
* Dispatch to have the CBUS module working only
|
|
* once connected.
|
|
*/
|
|
mhl_cbus_isr();
|
|
int_1_isr();
|
|
}
|
|
clear_all_intrs();
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void __exit mhl_msm_exit(void)
|
|
{
|
|
pr_warn("MHL: Exiting, Bye\n");
|
|
/*
|
|
* Delete driver if i2c client structure is NULL
|
|
*/
|
|
i2c_del_driver(&mhl_sii_i2c_driver);
|
|
if (!mhl_msm_state) {
|
|
kfree(mhl_msm_state);
|
|
mhl_msm_state = NULL;
|
|
}
|
|
}
|
|
|
|
module_init(mhl_msm_init);
|
|
module_exit(mhl_msm_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("MHL SII 8334 TX driver");
|