/*
* ti_hdmi_4xxx_ip.c
*
* HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
* Authors: Yong Zhi
* Mythri pk <mythripk@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "HDMICORE"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/sys_soc.h>
#include <sound/asound.h>
#include <sound/asoundef.h>
#include "hdmi4_core.h"
#define HDMI_CORE_AV 0x500
static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core)
{
return core->base + HDMI_CORE_AV;
}
static int hdmi_core_ddc_init(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
/* Turn on CLK for DDC */
REG_FLD_MOD(base, HDMI_CORE_AV_DPD, 0x7, 2, 0);
/* IN_PROG */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 1) {
/* Abort transaction */
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xf, 3, 0);
/* IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout aborting DDC transaction\n");
return -ETIMEDOUT;
}
}
/* Clk SCL Devices */
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xA, 3, 0);
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout starting SCL clock\n");
return -ETIMEDOUT;
}
/* Clear FIFO */
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x9, 3, 0);
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout clearing DDC fifo\n");
return -ETIMEDOUT;
}
return 0;
}
static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
u8 *pedid, int ext)
{
void __iomem *base = core->base;
u32 i;
char checksum;
u32 offset = 0;
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout waiting DDC to be ready\n");
return -ETIMEDOUT;
}
if (ext % 2 != 0)
offset = 0x80;
/* Load Segment Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0);
/* Load Slave Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1);
/* Load Offset Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0);
/* Load Byte Count */
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0);
/* Set DDC_CMD */
if (ext)
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0);
else
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0);
/* HDMI_CORE_DDC_STATUS_BUS_LOW */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 6, 6) == 1) {
DSSERR("I2C Bus Low?\n");
return -EIO;
}
/* HDMI_CORE_DDC_STATUS_NO_ACK */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 5, 5) == 1) {
DSSERR("I2C No Ack\n");
return -EIO;
}
for (i = 0; i < 0x80; ++i) {
int t;
/* IN_PROG */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 0) {
DSSERR("operation stopped when reading edid\n");
return -EIO;
}
t = 0;
/* FIFO_EMPTY */
while (REG_GET(base, HDMI_CORE_DDC_STATUS, 2, 2) == 1) {
if (t++ > 10000) {
DSSERR("timeout reading edid\n");
return -ETIMEDOUT;
}
udelay(1);
}
pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0);
}
checksum = 0;