blob: 14b15dedbf2ac9ca071322d769bc9258d7ba184d [file] [log] [blame]
/*
* Copyright (C) ST-Ericsson AB 2010
*
* ST-Ericsson HDMI driver
*
* Author: Per Persson <per.xb.persson@stericsson.com>
* for ST-Ericsson.
*
* License terms: GNU General Public License (GPL), version 2.
*/
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <video/av8100.h>
#include <video/hdmi.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/ctype.h>
#include "hdmi_loc.h"
#define SYSFS_EVENT_FILENAME "evread"
DEFINE_MUTEX(hdmi_events_mutex);
#define LOCK_HDMI_EVENTS mutex_lock(&hdmi_events_mutex)
#define UNLOCK_HDMI_EVENTS mutex_unlock(&hdmi_events_mutex)
#define EVENTS_MASK 0xFF
static int device_open;
static int events;
static int events_mask;
static bool events_received;
static wait_queue_head_t hdmi_event_wq;
struct device *hdmidev;
static ssize_t store_storeastext(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t store_plugdeten(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t store_edidread(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t show_edidread(struct device *dev, struct device_attribute *attr,
char *buf);
static ssize_t store_ceceven(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t show_cecread(struct device *dev, struct device_attribute *attr,
char *buf);
static ssize_t store_cecsend(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t store_infofrsend(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t store_hdcpeven(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t show_hdcpchkaesotp(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t store_hdcpfuseaes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t show_hdcpfuseaes(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t store_hdcploadaes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t show_hdcploadaes(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t store_hdcpauthencr(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t show_hdcpstateget(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t show_evread(struct device *dev, struct device_attribute *attr,
char *buf);
static ssize_t store_evclr(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t store_audiocfg(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static DEVICE_ATTR(storeastext, S_IWUSR, NULL, store_storeastext);
static DEVICE_ATTR(plugdeten, S_IWUSR, NULL, store_plugdeten);
static DEVICE_ATTR(edidread, S_IRUGO | S_IWUSR, show_edidread, store_edidread);
static DEVICE_ATTR(ceceven, S_IWUSR, NULL, store_ceceven);
static DEVICE_ATTR(cecread, S_IRUGO, show_cecread, NULL);
static DEVICE_ATTR(cecsend, S_IWUSR, NULL, store_cecsend);
static DEVICE_ATTR(infofrsend, S_IWUSR, NULL, store_infofrsend);
static DEVICE_ATTR(hdcpeven, S_IWUSR, NULL, store_hdcpeven);
static DEVICE_ATTR(hdcpchkaesotp, S_IRUGO, show_hdcpchkaesotp, NULL);
static DEVICE_ATTR(hdcpfuseaes, S_IRUGO | S_IWUSR, show_hdcpfuseaes,
store_hdcpfuseaes);
static DEVICE_ATTR(hdcploadaes, S_IRUGO | S_IWUSR, show_hdcploadaes,
store_hdcploadaes);
static DEVICE_ATTR(hdcpauthencr, S_IWUSR, NULL, store_hdcpauthencr);
static DEVICE_ATTR(hdcpstateget, S_IRUGO, show_hdcpstateget, NULL);
static DEVICE_ATTR(evread, S_IRUGO, show_evread, NULL);
static DEVICE_ATTR(evclr, S_IWUSR, NULL, store_evclr);
static DEVICE_ATTR(audiocfg, S_IWUSR, NULL, store_audiocfg);
/* Hex to int conversion */
static unsigned int htoi(const char *ptr)
{
unsigned int value = 0;
char ch = *ptr;
if (!ptr)
return 0;
if (isdigit(ch))
value = ch - '0';
else
value = toupper(ch) - 'A' + 10;
value <<= 4;
ch = *(++ptr);
if (isdigit(ch))
value += ch - '0';
else
value += toupper(ch) - 'A' + 10;
return value;
}
static int event_enable(bool enable, enum hdmi_event ev)
{
dev_dbg(hdmidev, "enable_event %d %02x\n", enable, ev);
if (enable)
events_mask |= ev;
else
events_mask &= ~ev;
return 0;
}
static int plugdeten(struct plug_detect *pldet)
{
struct av8100_status status;
u8 denc_off_time = 0;
int retval;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
av8100_reg_hdmi_5_volt_time_r(&denc_off_time, NULL, NULL);
retval = av8100_reg_hdmi_5_volt_time_w(
denc_off_time,
pldet->hdmi_off_time,
pldet->on_time);
if (retval) {
dev_err(hdmidev, "Failed to write the value to av8100 "
"register\n");
return -EFAULT;
}
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_IDLE) {
av8100_disable_interrupt();
av8100_enable_interrupt();
}
event_enable(pldet->hdmi_detect_enable != 0,
HDMI_EVENT_HDMI_PLUGIN);
event_enable(pldet->hdmi_detect_enable != 0,
HDMI_EVENT_HDMI_PLUGOUT);
return retval;
}
static int edidread(struct edid_read *edidread, u8 *len, u8 *data)
{
union av8100_configuration config;
struct av8100_status status;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
config.edid_section_readback_format.address = edidread->address;
config.edid_section_readback_format.block_number = edidread->block_nr;
dev_dbg(hdmidev, "addr:%0x blnr:%0x",
config.edid_section_readback_format.address,
config.edid_section_readback_format.block_number);
if (av8100_conf_prep(AV8100_COMMAND_EDID_SECTION_READBACK,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_EDID_SECTION_READBACK,
len,
data, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
dev_dbg(hdmidev, "len:%0x\n", *len);
return 0;
}
static int cecread(u8 *src, u8 *dest, u8 *data_len, u8 *data)
{
union av8100_configuration config;
struct av8100_status status;
u8 buf_len;
u8 buff[HDMI_CEC_READ_MAXSIZE];
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
if (av8100_conf_prep(AV8100_COMMAND_CEC_MESSAGE_READ_BACK,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_CEC_MESSAGE_READ_BACK,
&buf_len, buff, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
if (buf_len > 0) {
*src = (buff[0] & 0xF0) >> 4;
*dest = buff[0] & 0x0F;
*data_len = buf_len - 1;
memcpy(data, &buff[1], buf_len - 1);
} else
*data_len = 0;
return 0;
}
static int cecsend(u8 src, u8 dest, u8 data_len, u8 *data)
{
union av8100_configuration config;
struct av8100_status status;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
if (data_len < 1)
return -EINVAL;
config.cec_message_write_format.buffer[0] = ((src & 0x0F) << 4) +
(dest & 0x0F);
config.cec_message_write_format.buffer_length = data_len + 1;
memcpy(&config.cec_message_write_format.buffer[1], data, data_len - 1);
if (av8100_conf_prep(AV8100_COMMAND_CEC_MESSAGE_WRITE,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_CEC_MESSAGE_WRITE,
NULL, NULL, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
return 0;
}
static int infofrsend(u8 type, u8 version, u8 crc, u8 data_len, u8 *data)
{
union av8100_configuration config;
struct av8100_status status;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
if ((data_len < 1) || (data_len > HDMI_INFOFRAME_MAX_SIZE))
return -EINVAL;
config.infoframes_format.type = type;
config.infoframes_format.version = version;
config.infoframes_format.crc = crc;
config.infoframes_format.length = data_len;
memcpy(&config.infoframes_format.data, data, data_len);
if (av8100_conf_prep(AV8100_COMMAND_INFOFRAMES,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_INFOFRAMES,
NULL, NULL, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
return 0;
}
static int hdcpchkaesotp(u8 *crc, u8 *progged)
{
union av8100_configuration config;
struct av8100_status status;
u8 buf_len;
u8 buf[2];
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) !=
0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
config.fuse_aes_key_format.fuse_operation = AV8100_FUSE_READ;
memset(config.fuse_aes_key_format.key, 0, AV8100_FUSE_KEY_SIZE);
if (av8100_conf_prep(AV8100_COMMAND_FUSE_AES_KEY,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_FUSE_AES_KEY,
&buf_len, buf, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
if (buf_len == 2) {
*crc = buf[0];
*progged = buf[1];
}
return 0;
}
static int hdcpfuseaes(u8 *key, u8 crc, u8 *result)
{
union av8100_configuration config;
struct av8100_status status;
u8 buf_len;
u8 buf[2];
/* Default not OK */
*result = 1;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) !=
0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
config.fuse_aes_key_format.fuse_operation = AV8100_FUSE_WRITE;
memcpy(config.fuse_aes_key_format.key, key, AV8100_FUSE_KEY_SIZE);
if (av8100_conf_prep(AV8100_COMMAND_FUSE_AES_KEY,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_FUSE_AES_KEY,
&buf_len, buf, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
if (buf_len == 2) {
if ((crc == buf[0]) && (buf[1] == 1))
/* OK */
*result = 0;
}
return 0;
}
static int hdcploadaes(u8 block, u8 key_len, u8 *key, u8 *result)
{
union av8100_configuration config;
struct av8100_status status;
/* Default not OK */
*result = 1;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) !=
0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
config.hdcp_send_key_format.key_number = block;
config.hdcp_send_key_format.data_len = key_len;
memcpy(config.hdcp_send_key_format.data, key, key_len);
if (av8100_conf_prep(AV8100_COMMAND_HDCP_SENDKEY, &config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_HDCP_SENDKEY,
NULL, NULL, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
*result = 0;
return 0;
}
static int hdcpauthencr(u8 auth_type, u8 encr_type)
{
union av8100_configuration config;
struct av8100_status status;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
switch (auth_type) {
case HDMI_HDCP_AUTH_OFF:
default:
config.hdcp_management_format.req_type =
AV8100_HDCP_AUTH_REQ_OFF;
break;
case HDMI_HDCP_AUTH_START:
config.hdcp_management_format.req_type =
AV8100_HDCP_AUTH_REQ_ON;
break;
case HDMI_HDCP_AUTH_CONT:
config.hdcp_management_format.req_type =
AV8100_HDCP_AUTH_CONT;
break;
}
switch (encr_type) {
case HDMI_HDCP_ENCR_OFF:
config.hdcp_management_format.req_encr =
AV8100_HDCP_ENCR_REQ_OFF;
config.hdcp_management_format.encr_use =
AV8100_HDCP_ENCR_USE_OESS;
break;
case HDMI_HDCP_ENCR_OESS:
config.hdcp_management_format.req_encr =
AV8100_HDCP_ENCR_REQ_ON;
config.hdcp_management_format.encr_use =
AV8100_HDCP_ENCR_USE_OESS;
break;
case HDMI_HDCP_ENCR_EESS:
config.hdcp_management_format.req_encr =
AV8100_HDCP_ENCR_REQ_ON;
config.hdcp_management_format.encr_use =
AV8100_HDCP_ENCR_USE_EESS;
break;
}
if (av8100_conf_prep(AV8100_COMMAND_HDCP_MANAGEMENT,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_HDCP_MANAGEMENT,
NULL, NULL, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
return 0;
}
static u8 events_read(void)
{
int ret;
LOCK_HDMI_EVENTS;
ret = events;
dev_dbg(hdmidev, "%s %02x\n", __func__, events);
UNLOCK_HDMI_EVENTS;
return ret;
}
static int events_clear(u8 ev)
{
dev_dbg(hdmidev, "%s %02x\n", __func__, ev);
LOCK_HDMI_EVENTS;
events &= ~ev & EVENTS_MASK;
UNLOCK_HDMI_EVENTS;
return 0;
}
static int audiocfg(struct audio_cfg *cfg)
{
union av8100_configuration config;
struct av8100_status status;
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup failed\n");
return -EINVAL;
}
}
if (status.av8100_state < AV8100_OPMODE_INIT) {
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
}
config.audio_input_format.audio_input_if_format = cfg->if_format;
config.audio_input_format.i2s_input_nb = cfg->i2s_entries;
config.audio_input_format.sample_audio_freq = cfg->freq;
config.audio_input_format.audio_word_lg = cfg->word_length;
config.audio_input_format.audio_format = cfg->format;
config.audio_input_format.audio_if_mode = cfg->if_mode;
config.audio_input_format.audio_mute = cfg->mute;
if (av8100_conf_prep(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
&config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
NULL, NULL, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
return 0;
}
/* sysfs */
static ssize_t store_storeastext(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if ((count != HDMI_STOREASTEXT_BIN_SIZE) &&
(count != HDMI_STOREASTEXT_TEXT_SIZE) &&
(count != HDMI_STOREASTEXT_TEXT_SIZE + 1))
return -EINVAL;
if ((count == HDMI_STOREASTEXT_BIN_SIZE) && (*buf == 0x1))
hdmi_driver_data->store_as_hextext = true;
else if (((count == HDMI_STOREASTEXT_TEXT_SIZE) ||
(count == HDMI_STOREASTEXT_TEXT_SIZE + 1)) && (*buf == '0') &&
(*(buf + 1) == '1'))
hdmi_driver_data->store_as_hextext = true;
dev_dbg(hdmidev, "store_as_hextext:%0d\n",
hdmi_driver_data->store_as_hextext);
return count;
}
static ssize_t store_plugdeten(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct plug_detect plug_detect;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_PLUGDETEN_TEXT_SIZE) &&
(count != HDMI_PLUGDETEN_TEXT_SIZE + 1))
return -EINVAL;
plug_detect.hdmi_detect_enable = htoi(buf + index);
index += 2;
plug_detect.on_time = htoi(buf + index);
index += 2;
plug_detect.hdmi_off_time = htoi(buf + index);
index += 2;
} else {
if (count != HDMI_PLUGDETEN_BIN_SIZE)
return -EINVAL;
plug_detect.hdmi_detect_enable = *(buf + index++);
plug_detect.on_time = *(buf + index++);
plug_detect.hdmi_off_time = *(buf + index++);
}
if (plugdeten(&plug_detect))
return -EINVAL;
return count;
}
static ssize_t store_edidread(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct edid_read edid_read;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
dev_dbg(hdmidev, "count:%d\n", count);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_EDIDREAD_TEXT_SIZE) &&
(count != HDMI_EDIDREAD_TEXT_SIZE + 1))
return -EINVAL;
edid_read.address = htoi(buf + index);
index += 2;
edid_read.block_nr = htoi(buf + index);
index += 2;
} else {
if (count != HDMI_EDIDREAD_BIN_SIZE)
return -EINVAL;
edid_read.address = *(buf + index++);
edid_read.block_nr = *(buf + index++);
}
if (edidread(&edid_read, &hdmi_driver_data->edid_data.buf_len,
hdmi_driver_data->edid_data.buf))
return -EINVAL;
return count;
}
static ssize_t show_edidread(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
int len;
int index = 0;
int cnt;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
len = hdmi_driver_data->edid_data.buf_len;
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x", len);
index += 2;
} else
*(buf + index++) = len;
dev_dbg(hdmidev, "len:%02x\n", len);
cnt = 0;
while (cnt < len) {
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x",
hdmi_driver_data->edid_data.buf[cnt]);
index += 2;
} else
*(buf + index++) =
hdmi_driver_data->edid_data.buf[cnt];
dev_dbg(hdmidev, "%02x ",
hdmi_driver_data->edid_data.buf[cnt]);
cnt++;
}
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
return index;
}
static ssize_t store_ceceven(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
bool enable = false;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_CECEVEN_TEXT_SIZE) &&
(count != HDMI_CECEVEN_TEXT_SIZE + 1))
return -EINVAL;
if ((*buf == '0') && (*(buf + 1) == '1'))
enable = true;
} else {
if (count != HDMI_CECEVEN_BIN_SIZE)
return -EINVAL;
if (*buf == 0x01)
enable = true;
}
event_enable(enable, HDMI_EVENT_CEC);
return count;
}
static ssize_t show_cecread(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
struct cec_rw cec_read;
int index = 0;
int cnt;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (cecread(&cec_read.src, &cec_read.dest, &cec_read.length,
cec_read.data))
return -EINVAL;
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x", cec_read.src);
index += 2;
snprintf(buf + index, 3, "%02x", cec_read.dest);
index += 2;
snprintf(buf + index, 3, "%02x", cec_read.length);
} else {
*(buf + index++) = cec_read.src;
*(buf + index++) = cec_read.dest;
*(buf + index++) = cec_read.length;
}
dev_dbg(hdmidev, "len:%02x\n", cec_read.length);
cnt = 0;
while (cnt < cec_read.length) {
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x", cec_read.data[cnt]);
index += 2;
} else
*(buf + index++) = cec_read.data[cnt];
dev_dbg(hdmidev, "%02x ", cec_read.data[cnt]);
cnt++;
}
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
return index;
}
static ssize_t store_cecsend(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct cec_rw cec_w;
int index = 0;
int cnt;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count < HDMI_CECSEND_TEXT_SIZE_MIN) ||
(count > HDMI_CECSEND_TEXT_SIZE_MAX))
return -EINVAL;
cec_w.src = htoi(buf + index);
index += 2;
cec_w.dest = htoi(buf + index);
index += 2;
cec_w.length = htoi(buf + index);
index += 2;
if (cec_w.length > HDMI_CEC_WRITE_MAXSIZE)
return -EINVAL;
cnt = 0;
while (cnt < cec_w.length) {
cec_w.data[cnt] = htoi(buf + index);
index += 2;
dev_dbg(hdmidev, "%02x ", cec_w.data[cnt]);
cnt++;
}
} else {
if ((count < HDMI_CECSEND_BIN_SIZE_MIN) ||
(count > HDMI_CECSEND_BIN_SIZE_MAX))
return -EINVAL;
cec_w.src = *(buf + index++);
cec_w.dest = *(buf + index++);
cec_w.length = *(buf + index++);
if (cec_w.length > HDMI_CEC_WRITE_MAXSIZE)
return -EINVAL;
memcpy(cec_w.data, buf + index, cec_w.length);
}
if (cecsend(cec_w.src,
cec_w.dest,
cec_w.length,
cec_w.data))
return -EINVAL;
return count;
}
static ssize_t store_infofrsend(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct info_fr info_fr;
int index = 0;
int cnt;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count < HDMI_INFOFRSEND_TEXT_SIZE_MIN) ||
(count > HDMI_INFOFRSEND_TEXT_SIZE_MAX))
return -EINVAL;
info_fr.type = htoi(&buf[index]);
index += 2;
info_fr.ver = htoi(&buf[index]);
index += 2;
info_fr.crc = htoi(&buf[index]);
index += 2;
info_fr.length = htoi(&buf[index]);
index += 2;
if (info_fr.length > HDMI_INFOFRAME_MAX_SIZE)
return -EINVAL;
cnt = 0;
while (cnt < info_fr.length) {
info_fr.data[cnt] = htoi(buf + index);
index += 2;
dev_dbg(hdmidev, "%02x ", info_fr.data[cnt]);
cnt++;
}
} else {
if ((count < HDMI_INFOFRSEND_BIN_SIZE_MIN) ||
(count > HDMI_INFOFRSEND_BIN_SIZE_MAX))
return -EINVAL;
info_fr.type = *(buf + index++);
info_fr.ver = *(buf + index++);
info_fr.crc = *(buf + index++);
info_fr.length = *(buf + index++);
if (info_fr.length > HDMI_INFOFRAME_MAX_SIZE)
return -EINVAL;
memcpy(info_fr.data, buf + index, info_fr.length);
}
if (infofrsend(info_fr.type, info_fr.ver, info_fr.crc,
info_fr.length, info_fr.data))
return -EINVAL;
return count;
}
static ssize_t store_hdcpeven(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
bool enable = false;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_HDCPEVEN_TEXT_SIZE) &&
(count != HDMI_HDCPEVEN_TEXT_SIZE + 1))
return -EINVAL;
if ((*buf == '0') && (*(buf + 1) == '1'))
enable = true;
} else {
if (count != HDMI_HDCPEVEN_BIN_SIZE)
return -EINVAL;
if (*buf == 0x01)
enable = true;
}
event_enable(enable, HDMI_EVENT_HDCP);
return count;
}
static ssize_t show_hdcpchkaesotp(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
u8 crc;
u8 status;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdcpchkaesotp(&crc, &status))
return -EINVAL;
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x", status);
index += 2;
} else {
*(buf + index++) = status;
}
dev_dbg(hdmidev, "status:%02x\n", status);
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
return index;
}
static ssize_t store_hdcpfuseaes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct hdcp_fuseaes hdcp_fuseaes;
int index = 0;
int cnt;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_HDCP_FUSEAES_TEXT_SIZE) &&
(count != HDMI_HDCP_FUSEAES_TEXT_SIZE + 1))
return -EINVAL;
cnt = 0;
while (cnt < HDMI_HDCP_FUSEAES_KEYSIZE) {
hdcp_fuseaes.key[cnt] = htoi(buf + index);
index += 2;
dev_dbg(hdmidev, "%02x ", hdcp_fuseaes.key[cnt]);
cnt++;
}
hdcp_fuseaes.crc = htoi(&buf[index]);
index += 2;
} else {
if (count != HDMI_HDCP_FUSEAES_BIN_SIZE)
return -EINVAL;
memcpy(hdcp_fuseaes.key, buf + index,
HDMI_HDCP_FUSEAES_KEYSIZE);
index += HDMI_HDCP_FUSEAES_KEYSIZE;
hdcp_fuseaes.crc = *(buf + index++);
}
if (hdcpfuseaes(hdcp_fuseaes.key, hdcp_fuseaes.crc,
&hdcp_fuseaes.result))
return -EINVAL;
hdmi_driver_data->fuse_result = hdcp_fuseaes.result;
return count;
}
static ssize_t show_hdcpfuseaes(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x",
hdmi_driver_data->fuse_result);
index += 2;
} else
*(buf + index++) = hdmi_driver_data->fuse_result;
dev_dbg(hdmidev, "status:%02x\n", hdmi_driver_data->fuse_result);
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
return index;
}
static ssize_t store_hdcploadaes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct hdcp_loadaesone hdcp_loadaes;
int index = 0;
int block_cnt;
int cnt;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
/* Default not OK */
hdmi_driver_data->loadaes_result = 1;
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_HDCP_LOADAES_TEXT_SIZE) &&
(count != HDMI_HDCP_LOADAES_TEXT_SIZE + 1))
return -EINVAL;
/* AES */
block_cnt = 0;
while (block_cnt < HDMI_HDCP_AES_NR_OF_BLOCKS) {
cnt = 0;
while (cnt < HDMI_HDCP_AES_KEYSIZE) {
hdcp_loadaes.key[cnt] = htoi(buf + index);
index += 2;
dev_dbg(hdmidev, "%02x ",
hdcp_loadaes.key[cnt]);
cnt++;
}
if (hdcploadaes(block_cnt + HDMI_HDCP_AES_BLOCK_START,
HDMI_HDCP_AES_KEYSIZE,
hdcp_loadaes.key,
&hdcp_loadaes.result))
return -EINVAL;
if (hdcp_loadaes.result)
return -EINVAL;
block_cnt++;
}
/* KSV */
memset(hdcp_loadaes.key, 0, HDMI_HDCP_AES_KSVZEROESSIZE);
cnt = HDMI_HDCP_AES_KSVZEROESSIZE;
while (cnt < HDMI_HDCP_AES_KSVSIZE +
HDMI_HDCP_AES_KSVZEROESSIZE) {
hdcp_loadaes.key[cnt] =
htoi(&buf[index]);
index += 2;
dev_dbg(hdmidev, "%02x ", hdcp_loadaes.key[cnt]);
cnt++;
}
if (hdcploadaes(HDMI_HDCP_KSV_BLOCK,
HDMI_HDCP_AES_KSVSIZE +
HDMI_HDCP_AES_KSVZEROESSIZE,
hdcp_loadaes.key,
&hdcp_loadaes.result))
return -EINVAL;
if (hdcp_loadaes.result)
return -EINVAL;
} else {
if (count != HDMI_HDCP_LOADAES_BIN_SIZE)
return -EINVAL;
/* AES */
block_cnt = 0;
while (block_cnt < HDMI_HDCP_AES_NR_OF_BLOCKS) {
memcpy(hdcp_loadaes.key, buf + index,
HDMI_HDCP_AES_KEYSIZE);
index += HDMI_HDCP_AES_KEYSIZE;
if (hdcploadaes(block_cnt + HDMI_HDCP_AES_BLOCK_START,
HDMI_HDCP_AES_KEYSIZE,
hdcp_loadaes.key,
&hdcp_loadaes.result))
return -EINVAL;
if (hdcp_loadaes.result)
return -EINVAL;
}
/* KSV */
memset(hdcp_loadaes.key, 0, HDMI_HDCP_AES_KSVZEROESSIZE);
memcpy(hdcp_loadaes.key + HDMI_HDCP_AES_KSVZEROESSIZE,
buf + index,
HDMI_HDCP_AES_KSVSIZE);
index += HDMI_HDCP_AES_KSVSIZE;
if (hdcploadaes(HDMI_HDCP_KSV_BLOCK,
HDMI_HDCP_AES_KSVSIZE +
HDMI_HDCP_AES_KSVZEROESSIZE,
hdcp_loadaes.key,
&hdcp_loadaes.result))
return -EINVAL;
if (hdcp_loadaes.result)
return -EINVAL;
}
hdmi_driver_data->loadaes_result = hdcp_loadaes.result;
return count;
}
static ssize_t show_hdcploadaes(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x",
hdmi_driver_data->loadaes_result);
index += 2;
} else
*(buf + index++) = hdmi_driver_data->loadaes_result;
dev_dbg(hdmidev, "status:%02x\n", hdmi_driver_data->loadaes_result);
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
return index;
}
static ssize_t store_hdcpauthencr(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct hdcp_authencr hdcp_authencr;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_HDCPAUTHENCR_TEXT_SIZE) &&
(count != HDMI_HDCPAUTHENCR_TEXT_SIZE + 1))
return -EINVAL;
hdcp_authencr.auth_type = htoi(buf + index);
index += 2;
hdcp_authencr.encr_type = htoi(buf + index);
index += 2;
} else {
if (count != HDMI_HDCPAUTHENCR_BIN_SIZE)
return -EINVAL;
hdcp_authencr.auth_type = *(buf + index++);
hdcp_authencr.encr_type = *(buf + index++);
}
if (hdcpauthencr(hdcp_authencr.auth_type, hdcp_authencr.encr_type))
return -EINVAL;
return count;
}
static ssize_t show_hdcpstateget(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
u8 hdcp_state;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (av8100_reg_gen_status_r(NULL, NULL, NULL, NULL, &hdcp_state))
return -EINVAL;
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x", hdcp_state);
index += 2;
} else
*(buf + index++) = hdcp_state;
dev_dbg(hdmidev, "status:%02x\n", hdcp_state);
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
return index;
}
static ssize_t show_evread(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hdmi_driver_data *hdmi_driver_data;
int index = 0;
u8 ev;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
ev = events_read();
if (hdmi_driver_data->store_as_hextext) {
snprintf(buf + index, 3, "%02x", ev & 0xFF);
index += 2;
} else
*(buf + index++) = ev & 0xFF;
if (hdmi_driver_data->store_as_hextext)
*(buf + index++) = '\0';
/* Events are read: clear events */
events_clear(EVENTS_MASK);
return index;
}
static ssize_t store_evclr(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
u8 ev;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_EVCLR_TEXT_SIZE) &&
(count != HDMI_EVCLR_TEXT_SIZE + 1))
return -EINVAL;
ev = htoi(&buf[index]);
index += 2;
} else {
if (count != HDMI_EVCLR_BIN_SIZE)
return -EINVAL;
ev = *(buf + index++);
}
events_clear(ev);
return count;
}
static ssize_t store_audiocfg(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hdmi_driver_data *hdmi_driver_data;
struct audio_cfg audio_cfg;
int index = 0;
dev_dbg(hdmidev, "%s\n", __func__);
hdmi_driver_data = dev_get_drvdata(dev);
if (hdmi_driver_data->store_as_hextext) {
if ((count != HDMI_AUDIOCFG_TEXT_SIZE) &&
(count != HDMI_AUDIOCFG_TEXT_SIZE + 1))
return -EINVAL;
audio_cfg.if_format = htoi(&buf[index]);
index += 2;
audio_cfg.i2s_entries = htoi(&buf[index]);
index += 2;
audio_cfg.freq = htoi(&buf[index]);
index += 2;
audio_cfg.word_length = htoi(&buf[index]);
index += 2;
audio_cfg.format = htoi(&buf[index]);
index += 2;
audio_cfg.if_mode = htoi(&buf[index]);
index += 2;
audio_cfg.mute = htoi(&buf[index]);
index += 2;
} else {
if (count != HDMI_AUDIOCFG_BIN_SIZE)
return -EINVAL;
audio_cfg.if_format = *(buf + index++);
audio_cfg.i2s_entries = *(buf + index++);
audio_cfg.freq = *(buf + index++);
audio_cfg.word_length = *(buf + index++);
audio_cfg.format = *(buf + index++);
audio_cfg.if_mode = *(buf + index++);
audio_cfg.mute = *(buf + index++);
}
audiocfg(&audio_cfg);
return count;
}
static int hdmi_open(struct inode *inode, struct file *filp)
{
if (device_open)
return -EBUSY;
device_open++;
return 0;
}
static int hdmi_release(struct inode *inode, struct file *filp)
{
if (device_open)
device_open--;
return 0;
}
/* ioctl */
static int hdmi_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
u8 value = 0;
struct plug_detect plug_detect;
struct edid_read edid_read;
struct cec_rw cec_read;
struct cec_rw cec_send;
struct info_fr info_fr;
struct hdcp_fuseaes hdcp_fuseaes;
struct hdcp_loadaesall hdcp_loadaesall;
int block_cnt;
struct hdcp_loadaesone hdcp_loadaesone;
struct hdcp_authencr hdcp_authencr;
struct audio_cfg audio_cfg;
union av8100_configuration config;
struct hdmi_register reg;
struct hdmi_command_register command_reg;
struct av8100_status status;
u8 aes_status;
switch (cmd) {
case IOC_PLUG_DETECT_ENABLE:
if (copy_from_user(&plug_detect, (void *)arg,
sizeof(struct plug_detect)))
return -EINVAL;
if (plugdeten(&plug_detect))
return -EINVAL;
break;
case IOC_EDID_READ:
if (copy_from_user(&edid_read, (void *)arg,
sizeof(struct edid_read)))
return -EINVAL;
if (edidread(&edid_read, &edid_read.data_length,
edid_read.data))
return -EINVAL;
if (copy_to_user((void *)arg, (void *)&edid_read,
sizeof(struct edid_read))) {
return -EINVAL;
}
break;
case IOC_CEC_EVENT_ENABLE:
if (copy_from_user(&value, (void *)arg, sizeof(u8)))
return -EINVAL;
event_enable(value != 0, HDMI_EVENT_CEC);
break;
case IOC_CEC_READ:
if (cecread(&cec_read.src, &cec_read.dest, &cec_read.length,
cec_read.data))
return -EINVAL;
if (copy_to_user((void *)arg, (void *)&cec_read,
sizeof(struct cec_rw))) {
return -EINVAL;
}
break;
case IOC_CEC_SEND:
if (copy_from_user(&cec_send, (void *)arg,
sizeof(struct cec_rw)))
return -EINVAL;
if (cecsend(cec_send.src,
cec_send.dest,
cec_send.length,
cec_send.data))
return -EINVAL;
break;
case IOC_INFOFRAME_SEND:
if (copy_from_user(&info_fr, (void *)arg,
sizeof(struct info_fr)))
return -EINVAL;
if (infofrsend(info_fr.type, info_fr.ver, info_fr.crc,
info_fr.length, info_fr.data))
return -EINVAL;
break;
case IOC_HDCP_EVENT_ENABLE:
if (copy_from_user(&value, (void *)arg, sizeof(u8)))
return -EINVAL;
event_enable(value != 0, HDMI_EVENT_HDCP);
break;
case IOC_HDCP_CHKAESOTP:
if (hdcpchkaesotp(&value, &aes_status))
return -EINVAL;
if (copy_to_user((void *)arg, (void *)&aes_status,
sizeof(u8))) {
return -EINVAL;
}
break;
case IOC_HDCP_FUSEAES:
if (copy_from_user(&hdcp_fuseaes, (void *)arg,
sizeof(struct hdcp_fuseaes)))
return -EINVAL;
if (hdcpfuseaes(hdcp_fuseaes.key, hdcp_fuseaes.crc,
&hdcp_fuseaes.result))
return -EINVAL;
if (copy_to_user((void *)arg, (void *)&hdcp_fuseaes,
sizeof(struct hdcp_fuseaes))) {
return -EINVAL;
}
break;
case IOC_HDCP_LOADAES:
if (copy_from_user(&hdcp_loadaesall, (void *)arg,
sizeof(struct hdcp_loadaesall)))
return -EINVAL;
/* AES */
block_cnt = 0;
while (block_cnt < HDMI_HDCP_AES_NR_OF_BLOCKS) {
memcpy(hdcp_loadaesone.key, hdcp_loadaesall.key +
block_cnt * HDMI_HDCP_AES_KEYSIZE,
HDMI_HDCP_AES_KEYSIZE);
if (hdcploadaes(block_cnt + HDMI_HDCP_AES_BLOCK_START,
HDMI_HDCP_AES_KEYSIZE,
hdcp_loadaesone.key,
&hdcp_loadaesone.result))
return -EINVAL;
if (hdcp_loadaesone.result)
return -EINVAL;
}
/* KSV */
memset(hdcp_loadaesone.key, 0, HDMI_HDCP_AES_KSVZEROESSIZE);
memcpy(hdcp_loadaesone.key + HDMI_HDCP_AES_KSVZEROESSIZE,
hdcp_loadaesall.ksv, HDMI_HDCP_AES_KSVSIZE);
if (hdcploadaes(HDMI_HDCP_KSV_BLOCK,
HDMI_HDCP_AES_KSVSIZE +
HDMI_HDCP_AES_KSVZEROESSIZE,
hdcp_loadaesone.key,
&hdcp_loadaesone.result))
return -EINVAL;
if (hdcp_loadaesone.result)
return -EINVAL;
hdcp_loadaesall.result = hdcp_loadaesone.result;
if (copy_to_user((void *)arg, (void *)&hdcp_loadaesall,
sizeof(struct hdcp_loadaesall))) {
return -EINVAL;
}
break;
case IOC_HDCP_AUTHENCR_REQ:
if (copy_from_user(&hdcp_authencr, (void *)arg,
sizeof(struct hdcp_authencr)))
return -EINVAL;
if (hdcpauthencr(hdcp_authencr.auth_type,
hdcp_authencr.encr_type))
return -EINVAL;
break;
case IOC_HDCP_STATE_GET:
if (av8100_reg_gen_status_r(NULL, NULL, NULL, NULL,
&value))
return -EINVAL;
if (copy_to_user((void *)arg, (void *)&value,
sizeof(u8))) {
return -EINVAL;
}
break;
case IOC_EVENTS_READ:
value = events_read();
if (copy_to_user((void *)arg, (void *)&value,
sizeof(u8))) {
return -EINVAL;
}
/* Events are read: clear events */
events_clear(EVENTS_MASK);
break;
case IOC_EVENTS_CLEAR:
if (copy_from_user(&value, (void *)arg, sizeof(u8)))
return -EINVAL;
events_clear(value);
break;
case IOC_AUDIO_CFG:
if (copy_from_user(&audio_cfg, (void *)arg,
sizeof(struct audio_cfg)))
return -EINVAL;
audiocfg(&audio_cfg);
break;
/* Internal */
case IOC_HDMI_POWER:
/* Get desired power state on or off */
if (copy_from_user(&value, (void *)arg, sizeof(u8)))
return -EINVAL;
if (value == 0) {
if (av8100_powerdown() != 0) {
dev_err(hdmidev, "av8100_powerdown FAIL\n");
return -EINVAL;
}
} else {
if (av8100_powerup() != 0) {
dev_err(hdmidev, "av8100_powerup FAIL\n");
return -EINVAL;
}
}
break;
case IOC_HDMI_ENABLE_INTERRUPTS:
av8100_disable_interrupt();
if (av8100_enable_interrupt() != 0) {
dev_err(hdmidev, "av8100_conf_get FAIL\n");
return -EINVAL;
}
break;
case IOC_HDMI_DOWNLOAD_FW:
if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100 dl fw FAIL\n");
return -EINVAL;
}
break;
case IOC_HDMI_ONOFF:
/* Get desired HDMI mode on or off */
if (copy_from_user(&value, (void *)arg, sizeof(u8)))
return -EFAULT;
if (av8100_conf_get(AV8100_COMMAND_HDMI, &config) != 0) {
dev_err(hdmidev, "av8100_conf_get FAIL\n");
return -EINVAL;
}
if (value == 0)
config.hdmi_format.hdmi_mode = AV8100_HDMI_OFF;
else
config.hdmi_format.hdmi_mode = AV8100_HDMI_ON;
if (av8100_conf_prep(AV8100_COMMAND_HDMI, &config) != 0) {
dev_err(hdmidev, "av8100_conf_prep FAIL\n");
return -EINVAL;
}
if (av8100_conf_w(AV8100_COMMAND_HDMI, NULL, NULL,
I2C_INTERFACE) != 0) {
dev_err(hdmidev, "av8100_conf_w FAIL\n");
return -EINVAL;
}
break;
case IOC_HDMI_REGISTER_WRITE:
if (copy_from_user(&reg, (void *)arg,
sizeof(struct hdmi_register))) {
return -EINVAL;
}
if (av8100_reg_w(reg.offset, reg.value) != 0) {
dev_err(hdmidev, "hdmi_register_write FAIL\n");
return -EINVAL;
}
break;
case IOC_HDMI_REGISTER_READ:
if (copy_from_user(&reg, (void *)arg,
sizeof(struct hdmi_register))) {
return -EINVAL;
}
if (av8100_reg_r(reg.offset, &reg.value) != 0) {
dev_err(hdmidev, "hdmi_register_write FAIL\n");
return -EINVAL;
}
if (copy_to_user((void *)arg, (void *)&reg,
sizeof(struct hdmi_register))) {
return -EINVAL;
}
break;
case IOC_HDMI_STATUS_GET:
status = av8100_status_get();
if (copy_to_user((void *)arg, (void *)&status,
sizeof(struct av8100_status))) {
return -EINVAL;
}
break;
case IOC_HDMI_CONFIGURATION_WRITE:
if (copy_from_user(&command_reg, (void *)arg,
sizeof(struct hdmi_command_register)) != 0) {
dev_err(hdmidev, "IOC_HDMI_CONFIGURATION_WRITE "
"fail 1\n");
command_reg.return_status = EINVAL;
} else {
if (av8100_conf_w_raw(command_reg.cmd_id,
command_reg.buf_len,
command_reg.buf,
&(command_reg.buf_len),
command_reg.buf) != 0) {
dev_err(hdmidev, "IOC_HDMI_CONFIGURATION_WRITE "
"fail 2\n");
command_reg.return_status = EINVAL;
}
}
if (copy_to_user((void *)arg, (void *)&command_reg,
sizeof(struct hdmi_command_register)) != 0) {
return -EINVAL;
}
break;
default:
break;
}
return 0;
}
static unsigned int
hdmi_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
dev_dbg(hdmidev, "%s\n", __func__);
poll_wait(filp, &hdmi_event_wq , wait);
LOCK_HDMI_EVENTS;
if (events_received == true) {
events_received = false;
mask = POLLIN | POLLRDNORM;
}
UNLOCK_HDMI_EVENTS;
return mask;
}
static const struct file_operations hdmi_fops = {
.owner = THIS_MODULE,
.open = hdmi_open,
.release = hdmi_release,
.ioctl = hdmi_ioctl,
.poll = hdmi_poll
};
static struct miscdevice hdmi_miscdev = {
MISC_DYNAMIC_MINOR,
"hdmi",
&hdmi_fops
};
/* Event callback function called by hw driver */
void hdmi_event(enum av8100_hdmi_event ev)
{
int events_old;
int events_new;
struct kobject *kobj = &hdmidev->kobj;
dev_dbg(hdmidev, "hdmi_event %02x\n", ev);
LOCK_HDMI_EVENTS;
events_old = events;
/* Set event */
switch (ev) {
case AV8100_HDMI_EVENT_HDMI_PLUGIN:
events |= events_mask & HDMI_EVENT_HDMI_PLUGIN;
break;
case AV8100_HDMI_EVENT_HDMI_PLUGOUT:
events |= events_mask & HDMI_EVENT_HDMI_PLUGOUT;
break;
case AV8100_HDMI_EVENT_CEC:
events |= events_mask & HDMI_EVENT_CEC;
break;
case AV8100_HDMI_EVENT_HDCP:
events |= events_mask & HDMI_EVENT_HDCP;
break;
default:
break;
}
events_new = events;
UNLOCK_HDMI_EVENTS;
dev_dbg(hdmidev, "hdmi events:%02x, events_old:%02x mask:%02x\n",
events_new, events_old, events_mask);
if (events_new != events_old) {
/* Wake up application waiting for event via call to poll() */
sysfs_notify(kobj, NULL, SYSFS_EVENT_FILENAME);
LOCK_HDMI_EVENTS;
events_received = true;
UNLOCK_HDMI_EVENTS;
wake_up_interruptible(&hdmi_event_wq);
}
}
int __init hdmi_init(void)
{
int ret;
struct hdmi_driver_data *hdmi_driver_data;
ret = misc_register(&hdmi_miscdev);
if (ret)
goto hdmi_init_out;
hdmidev = hdmi_miscdev.this_device;
hdmidev->driver_data =
kzalloc(sizeof(struct hdmi_driver_data), GFP_KERNEL);
if (!hdmidev->driver_data)
return -ENOMEM;
hdmi_driver_data = dev_get_drvdata(hdmidev);
/* Default sysfs file format is hextext */
hdmi_driver_data->store_as_hextext = true;
init_waitqueue_head(&hdmi_event_wq);
if (device_create_file(hdmidev, &dev_attr_storeastext))
dev_info(hdmidev, "Unable to create storeastext attribute\n");
if (device_create_file(hdmidev, &dev_attr_plugdeten))
dev_info(hdmidev, "Unable to create plugdeten attribute\n");
if (device_create_file(hdmidev, &dev_attr_edidread))
dev_info(hdmidev, "Unable to create edidread attribute\n");
if (device_create_file(hdmidev, &dev_attr_ceceven))
dev_info(hdmidev, "Unable to create ceceven attribute\n");
if (device_create_file(hdmidev, &dev_attr_cecread))
dev_info(hdmidev, "Unable to create cecread attribute\n");
if (device_create_file(hdmidev, &dev_attr_cecsend))
dev_info(hdmidev, "Unable to create cecsend attribute\n");
if (device_create_file(hdmidev, &dev_attr_infofrsend))
dev_info(hdmidev, "Unable to create infofrsend attribute\n");
if (device_create_file(hdmidev, &dev_attr_hdcpeven))
dev_info(hdmidev, "Unable to create hdcpeven attribute\n");
if (device_create_file(hdmidev, &dev_attr_hdcpchkaesotp))
dev_info(hdmidev, "Unable to create hdcpchkaesotp attribute\n");
if (device_create_file(hdmidev, &dev_attr_hdcpfuseaes))
dev_info(hdmidev, "Unable to create hdcpfuseaes attribute\n");
if (device_create_file(hdmidev, &dev_attr_hdcploadaes))
dev_info(hdmidev, "Unable to create hdcploadaes attribute\n");
if (device_create_file(hdmidev, &dev_attr_hdcpauthencr))
dev_info(hdmidev, "Unable to create hdcpauthreq attribute\n");
if (device_create_file(hdmidev, &dev_attr_hdcpstateget))
dev_info(hdmidev, "Unable to create hdcpstateget attribute\n");
if (device_create_file(hdmidev, &dev_attr_evread))
dev_info(hdmidev, "Unable to create evread attribute\n");
if (device_create_file(hdmidev, &dev_attr_evclr))
dev_info(hdmidev, "Unable to create evclr attribute\n");
if (device_create_file(hdmidev, &dev_attr_audiocfg))
dev_info(hdmidev, "Unable to create audiocfg attribute\n");
/* Register event callback */
av8100_hdmi_event_cb_set(hdmi_event);
hdmi_init_out:
return ret;
}
void hdmi_exit(void)
{
/* Deregister event callback */
av8100_hdmi_event_cb_set(NULL);
device_remove_file(hdmidev, &dev_attr_storeastext);
device_remove_file(hdmidev, &dev_attr_plugdeten);
device_remove_file(hdmidev, &dev_attr_edidread);
device_remove_file(hdmidev, &dev_attr_ceceven);
device_remove_file(hdmidev, &dev_attr_cecread);
device_remove_file(hdmidev, &dev_attr_cecsend);
device_remove_file(hdmidev, &dev_attr_infofrsend);
device_remove_file(hdmidev, &dev_attr_hdcpeven);
device_remove_file(hdmidev, &dev_attr_hdcpchkaesotp);
device_remove_file(hdmidev, &dev_attr_hdcpfuseaes);
device_remove_file(hdmidev, &dev_attr_hdcploadaes);
device_remove_file(hdmidev, &dev_attr_hdcpauthencr);
device_remove_file(hdmidev, &dev_attr_hdcpstateget);
device_remove_file(hdmidev, &dev_attr_evread);
device_remove_file(hdmidev, &dev_attr_evclr);
device_remove_file(hdmidev, &dev_attr_audiocfg);
kfree(hdmidev->driver_data);
misc_deregister(&hdmi_miscdev);
}