msm: add display bridge abstraction driver

Add display bridge abstraction driver to provide an abstract interface
for different types of bridge chips used on MSM platforms. This
interface allows driver development for bridge chips from different
manufactorers while allowing users, of the funcationality provided by
these bridge chips, to re-use the same code across different boards.

Change-Id: I64457e3a44479a3b1be5482691d4727a86dc9b87
Signed-off-by: Vinu Deokaran <vinud@codeaurora.org>
Signed-off-by: Siddharth Zaveri <szaveri@codeaurora.org>
[cip@codeaurora.org: Moved file locations]
Signed-off-by: Clarence Ip <cip@codeaurora.org>
This commit is contained in:
Vinu Deokaran 2015-04-03 14:19:04 -07:00 committed by David Keitel
parent b01c8e9849
commit 3d30cc8dde
10 changed files with 1861 additions and 0 deletions

View file

@ -1,3 +1,4 @@
source "drivers/video/fbdev/msm/msm_dba/Kconfig"
if FB_MSM

View file

@ -1,6 +1,7 @@
ccflags-y += -I$(src) -Idrivers/staging/android
obj-$(CONFIG_FB_MSM_MDSS_MHL3) += mhl3/
obj-$(CONFIG_MSM_DBA) += msm_dba/
mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o dsi_status_v2.o
mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o

View file

@ -0,0 +1,15 @@
#
# MSM DBA
#
config MSM_DBA
bool "MSM Display Bridge Abstraction support"
depends on ARM
---help---
Support for MSM display bridge abstraction interface. MSM display
drivers can use the same interface to interact with different third
party bridge chips. Drivers implemented for third party bridge chips
should support this interface to allow display driver to control the
bridge chip. The MSM DBA driver maintains a list of devices supported
on the platform and allow clients to register and access these
devices.

View file

@ -0,0 +1,3 @@
obj-$(CONFIG_MSM_DBA) += msm_dba.o msm_dba_init.o msm_dba_helpers.o msm_dba_debug.o
clean:
rm *.o

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <video/msm_dba.h>
#include <msm_dba_internal.h>
static DEFINE_MUTEX(register_mutex);
void *msm_dba_register_client(struct msm_dba_reg_info *info,
struct msm_dba_ops *ops)
{
int rc = 0;
struct msm_dba_device_info *device = NULL;
struct msm_dba_client_info *client = NULL;
pr_debug("%s: ENTER\n", __func__);
if (!info || !ops) {
pr_err("%s: Invalid params\n", __func__);
return ERR_PTR(-EINVAL);
}
mutex_lock(&register_mutex);
pr_debug("%s: Client(%s) Chip(%s) Instance(%d)\n", __func__,
info->client_name, info->chip_name, info->instance_id);
rc = msm_dba_get_probed_device(info, &device);
if (rc) {
pr_err("%s: Device not found (%s, %d)\n", __func__,
info->chip_name,
info->instance_id);
mutex_unlock(&register_mutex);
return ERR_PTR(rc);
}
pr_debug("%s: Client(%s) device found\n", __func__, info->client_name);
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
mutex_unlock(&register_mutex);
return ERR_PTR(-ENOMEM);
}
memset(client, 0x0, sizeof(*client));
client->dev = device;
strlcpy(client->client_name, info->client_name,
MSM_DBA_CLIENT_NAME_LEN);
client->cb = info->cb;
client->cb_data = info->cb_data;
mutex_lock_nested(&device->dev_mutex, SINGLE_DEPTH_NESTING);
list_add(&client->list, &device->client_list);
*ops = device->client_ops;
mutex_unlock(&device->dev_mutex);
if (device->reg_fxn) {
rc = device->reg_fxn(client);
if (rc) {
pr_err("%s: Client register failed (%s, %d)\n",
__func__, info->chip_name, info->instance_id);
kfree(client);
mutex_unlock(&register_mutex);
return ERR_PTR(rc);
}
}
mutex_unlock(&register_mutex);
pr_debug("%s: EXIT\n", __func__);
return client;
}
EXPORT_SYMBOL(msm_dba_register_client);
int msm_dba_deregister_client(void *client)
{
int rc = 0;
struct msm_dba_client_info *handle = client;
struct msm_dba_client_info *node = NULL;
struct list_head *tmp = NULL;
struct list_head *position = NULL;
pr_debug("%s: ENTER\n", __func__);
if (!handle) {
pr_err("%s: Invalid Params\n", __func__);
return -EINVAL;
}
mutex_lock(&register_mutex);
pr_debug("%s: Client(%s) Chip(%s) Instance(%d)\n", __func__,
handle->client_name, handle->dev->chip_name,
handle->dev->instance_id);
if (handle->dev->dereg_fxn) {
rc = handle->dev->dereg_fxn(handle);
if (rc) {
pr_err("%s: Client deregister failed (%s)\n",
__func__, handle->client_name);
}
}
mutex_lock_nested(&handle->dev->dev_mutex, SINGLE_DEPTH_NESTING);
list_for_each_safe(position, tmp, &handle->dev->client_list) {
node = list_entry(position, struct msm_dba_client_info, list);
if (node == handle) {
list_del(&node->list);
break;
}
}
mutex_unlock(&handle->dev->dev_mutex);
kfree(handle);
mutex_unlock(&register_mutex);
pr_debug("%s: EXIT (%d)\n", __func__, rc);
return rc;
}
EXPORT_SYMBOL(msm_dba_deregister_client);

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/i2c.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <video/msm_dba.h>
#include "msm_dba_internal.h"
static inline struct msm_dba_device_info *to_dba_dev(struct device *dev)
{
return dev_get_drvdata(dev);
}
static ssize_t device_name_rda_attr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
return snprintf(buf, PAGE_SIZE, "%s:%d\n", device->chip_name,
device->instance_id);
}
static ssize_t client_list_rda_attr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
struct msm_dba_client_info *c;
struct list_head *pos = NULL;
ssize_t bytes = 0;
mutex_lock(&device->dev_mutex);
list_for_each(pos, &device->client_list) {
c = list_entry(pos, struct msm_dba_client_info, list);
bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes), "%s\n",
c->client_name);
}
mutex_unlock(&device->dev_mutex);
return bytes;
}
static ssize_t power_status_rda_attr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
struct msm_dba_client_info *c;
struct list_head *pos = NULL;
ssize_t bytes = 0;
mutex_lock(&device->dev_mutex);
bytes = snprintf(buf, PAGE_SIZE, "power_status:%d\n",
device->power_status);
list_for_each(pos, &device->client_list) {
c = list_entry(pos, struct msm_dba_client_info, list);
bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes),
"client: %s, status = %d\n",
c->client_name, c->power_on);
}
mutex_unlock(&device->dev_mutex);
return bytes;
}
static ssize_t video_status_rda_attr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
struct msm_dba_client_info *c;
struct list_head *pos = NULL;
ssize_t bytes = 0;
mutex_lock(&device->dev_mutex);
bytes = snprintf(buf, PAGE_SIZE, "video_status:%d\n",
device->video_status);
list_for_each(pos, &device->client_list) {
c = list_entry(pos, struct msm_dba_client_info, list);
bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes),
"client: %s, status = %d\n",
c->client_name, c->video_on);
}
mutex_unlock(&device->dev_mutex);
return bytes;
}
static ssize_t audio_status_rda_attr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
struct msm_dba_client_info *c;
struct list_head *pos = NULL;
ssize_t bytes = 0;
mutex_lock(&device->dev_mutex);
bytes = snprintf(buf, PAGE_SIZE, "audio_status:%d\n",
device->audio_status);
list_for_each(pos, &device->client_list) {
c = list_entry(pos, struct msm_dba_client_info, list);
bytes += snprintf(buf + bytes, (PAGE_SIZE - bytes),
"client: %s, status = %d\n",
c->client_name, c->audio_on);
}
mutex_unlock(&device->dev_mutex);
return bytes;
}
static ssize_t write_reg_wta_attr(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
char *regstr, *valstr, *ptr;
char str[20];
long reg = 0;
long val = 0;
int rc = 0;
int len;
len = strlen(buf);
strlcpy(str, buf, 20);
if (len < 20)
str[len] = '\0';
else
str[19] = '\0';
ptr = str;
regstr = strsep(&ptr, ":");
valstr = strsep(&ptr, ":");
rc = kstrtol(regstr, 0, &reg);
if (rc) {
pr_err("%s: kstrol error %d\n", __func__, rc);
} else {
rc = kstrtol(valstr, 0, &val);
if (rc)
pr_err("%s: kstrol error for val %d\n", __func__, rc);
}
if (!rc) {
mutex_lock(&device->dev_mutex);
if (device->dev_ops.write_reg) {
rc = device->dev_ops.write_reg(device,
(u32)reg,
(u32)val);
if (rc) {
pr_err("%s: failed to write reg %d", __func__,
rc);
}
} else {
pr_err("%s: not supported\n", __func__);
}
mutex_unlock(&device->dev_mutex);
}
return count;
}
static ssize_t read_reg_rda_attr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
ssize_t bytes;
mutex_lock(&device->dev_mutex);
bytes = snprintf(buf, PAGE_SIZE, "0x%x\n", device->register_val);
mutex_unlock(&device->dev_mutex);
return bytes;
}
static ssize_t read_reg_wta_attr(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
long reg = 0;
int rc = 0;
u32 val = 0;
rc = kstrtol(buf, 0, &reg);
if (rc) {
pr_err("%s: kstrol error %d\n", __func__, rc);
} else {
mutex_lock(&device->dev_mutex);
if (device->dev_ops.read_reg) {
rc = device->dev_ops.read_reg(device,
(u32)reg,
&val);
if (rc) {
pr_err("%s: failed to write reg %d", __func__,
rc);
} else {
device->register_val = val;
}
} else {
pr_err("%s: not supported\n", __func__);
}
mutex_unlock(&device->dev_mutex);
}
return count;
}
static ssize_t dump_info_wta_attr(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct msm_dba_device_info *device = to_dba_dev(dev);
int rc;
rc = device->dev_ops.dump_debug_info(device, 0x00);
if (rc)
pr_err("%s: failed to dump debug data\n", __func__);
return count;
}
static DEVICE_ATTR(device_name, S_IRUGO, device_name_rda_attr, NULL);
static DEVICE_ATTR(client_list, S_IRUGO, client_list_rda_attr, NULL);
static DEVICE_ATTR(power_status, S_IRUGO, power_status_rda_attr, NULL);
static DEVICE_ATTR(video_status, S_IRUGO, video_status_rda_attr, NULL);
static DEVICE_ATTR(audio_status, S_IRUGO, audio_status_rda_attr, NULL);
static DEVICE_ATTR(write_reg, S_IWUSR, NULL, write_reg_wta_attr);
static DEVICE_ATTR(read_reg, S_IRUGO | S_IWUSR, read_reg_rda_attr,
read_reg_wta_attr);
static DEVICE_ATTR(dump_info, S_IWUSR, NULL, dump_info_wta_attr);
static struct attribute *msm_dba_sysfs_attrs[] = {
&dev_attr_device_name.attr,
&dev_attr_client_list.attr,
&dev_attr_power_status.attr,
&dev_attr_video_status.attr,
&dev_attr_audio_status.attr,
&dev_attr_write_reg.attr,
&dev_attr_read_reg.attr,
&dev_attr_dump_info.attr,
NULL,
};
static struct attribute_group msm_dba_sysfs_attr_grp = {
.attrs = msm_dba_sysfs_attrs,
};
int msm_dba_helper_sysfs_init(struct device *dev)
{
int rc = 0;
if (!dev) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
rc = sysfs_create_group(&dev->kobj, &msm_dba_sysfs_attr_grp);
if (rc)
pr_err("%s: sysfs group creation failed %d\n", __func__, rc);
return rc;
}

View file

@ -0,0 +1,445 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/i2c.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <video/msm_dba.h>
#include "msm_dba_internal.h"
static void msm_dba_helper_hdcp_handler(struct work_struct *work)
{
struct msm_dba_device_info *dev;
int rc = 0;
if (!work) {
pr_err("%s: Invalid params\n", __func__);
return;
}
dev = container_of(work, struct msm_dba_device_info, hdcp_work);
mutex_lock(&dev->dev_mutex);
if (dev->hdcp_status) {
pr_debug("%s[%s:%d] HDCP is authenticated\n", __func__,
dev->chip_name, dev->instance_id);
mutex_unlock(&dev->dev_mutex);
return;
}
if (dev->dev_ops.hdcp_reset) {
rc = dev->dev_ops.hdcp_reset(dev);
if (rc)
pr_err("%s[%s:%d] HDCP reset failed\n", __func__,
dev->chip_name, dev->instance_id);
}
if (dev->dev_ops.hdcp_retry) {
rc = dev->dev_ops.hdcp_retry(dev, MSM_DBA_ASYNC_FLAG);
if (rc)
pr_err("%s[%s:%d] HDCP retry failed\n", __func__,
dev->chip_name, dev->instance_id);
}
mutex_unlock(&dev->dev_mutex);
}
static void msm_dba_helper_issue_cb(struct msm_dba_device_info *dev,
struct msm_dba_client_info *client,
enum msm_dba_callback_event event)
{
struct msm_dba_client_info *c;
struct list_head *pos = NULL;
u32 user_mask = 0;
list_for_each(pos, &dev->client_list) {
c = list_entry(pos, struct msm_dba_client_info, list);
if (client && client == c)
continue;
user_mask = c->event_mask & event;
if (c->cb && user_mask)
c->cb(c->cb_data, user_mask);
}
}
static irqreturn_t msm_dba_helper_irq_handler(int irq, void *dev)
{
struct msm_dba_device_info *device = dev;
u32 mask = 0;
int rc = 0;
bool ret;
mutex_lock(&device->dev_mutex);
if (device->dev_ops.handle_interrupts) {
rc = device->dev_ops.handle_interrupts(device, &mask);
if (rc)
pr_err("%s: interrupt handler failed\n", __func__);
}
pr_debug("%s(%s:%d): Eventmask = 0x%x\n", __func__, device->chip_name,
device->instance_id, mask);
if (mask)
msm_dba_helper_issue_cb(device, NULL, mask);
if ((mask & MSM_DBA_CB_HDCP_LINK_UNAUTHENTICATED) &&
device->hdcp_monitor_on) {
ret = queue_work(device->hdcp_wq, &device->hdcp_work);
if (!ret)
pr_err("%s: queue_work failed %d\n", __func__, rc);
}
if (device->dev_ops.unmask_interrupts)
rc = device->dev_ops.unmask_interrupts(device, mask);
mutex_unlock(&device->dev_mutex);
return IRQ_HANDLED;
}
int msm_dba_helper_i2c_write_byte(struct i2c_client *client,
u8 addr,
u8 reg,
u8 val)
{
int rc = 0;
struct i2c_msg msg;
u8 buf[2] = {reg, val};
if (!client) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
pr_debug("%s: [%s:0x02%x] : W[0x%02x, 0x%02x]\n", __func__,
client->name, addr, reg, val);
client->addr = addr;
msg.addr = addr;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
if (i2c_transfer(client->adapter, &msg, 1) < 1) {
pr_err("%s: i2c write failed\n", __func__);
rc = -EIO;
}
return rc;
}
int msm_dba_helper_i2c_write_buffer(struct i2c_client *client,
u8 addr,
u8 *buf,
u32 size)
{
int rc = 0;
struct i2c_msg msg;
if (!client) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
pr_debug("%s: [%s:0x02%x] : W %d bytes\n", __func__,
client->name, addr, size);
client->addr = addr;
msg.addr = addr;
msg.flags = 0;
msg.len = size;
msg.buf = buf;
if (i2c_transfer(client->adapter, &msg, 1) != 1) {
pr_err("%s: i2c write failed\n", __func__);
rc = -EIO;
}
return rc;
}
int msm_dba_helper_i2c_read(struct i2c_client *client,
u8 addr,
u8 reg,
char *buf,
u32 size)
{
int rc = 0;
struct i2c_msg msg[2];
if (!client || !buf) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
client->addr = addr;
msg[0].addr = addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &reg;
msg[1].addr = addr;
msg[1].flags = I2C_M_RD;
msg[1].len = size;
msg[1].buf = buf;
if (i2c_transfer(client->adapter, msg, 2) != 2) {
pr_err("%s: i2c read failed\n", __func__);
rc = -EIO;
}
pr_debug("%s: [%s:0x02%x] : R[0x%02x, 0x%02x]\n", __func__,
client->name, addr, reg, *buf);
return rc;
}
int msm_dba_helper_power_on(void *client, bool on, u32 flags)
{
int rc = 0;
struct msm_dba_client_info *c = client;
struct msm_dba_device_info *device;
struct msm_dba_client_info *node;
struct list_head *pos = NULL;
bool power_on = false;
if (!c) {
pr_err("%s: Invalid Params\n", __func__);
return -EINVAL;
}
device = c->dev;
mutex_lock(&device->dev_mutex);
/*
* Power on the device if atleast one client powers on the device. But
* power off will be done only after all the clients have called power
* off
*/
if (on == device->power_status) {
c->power_on = on;
} else if (on) {
rc = device->dev_ops.dev_power_on(device, on);
if (rc)
pr_err("%s:%s: power on failed\n", device->chip_name,
__func__);
else
c->power_on = on;
} else {
c->power_on = false;
list_for_each(pos, &device->client_list) {
node = list_entry(pos, struct msm_dba_client_info,
list);
if (c->power_on) {
power_on = true;
break;
}
}
if (!power_on) {
rc = device->dev_ops.dev_power_on(device, false);
if (rc) {
pr_err("%s:%s: power off failed\n",
device->chip_name, __func__);
c->power_on = true;
}
}
}
mutex_unlock(&device->dev_mutex);
return rc;
}
int msm_dba_helper_video_on(void *client, bool on,
struct msm_dba_video_cfg *cfg, u32 flags)
{
int rc = 0;
struct msm_dba_client_info *c = client;
struct msm_dba_device_info *device;
struct msm_dba_client_info *node;
struct list_head *pos = NULL;
bool video_on = false;
if (!c) {
pr_err("%s: Invalid Params\n", __func__);
return -EINVAL;
}
device = c->dev;
mutex_lock(&device->dev_mutex);
/*
* Video will be turned on if at least one client turns on video. But
* video off will be done only after all the clients have called video
* off
*/
if (on == device->video_status) {
c->video_on = on;
} else if (on) {
rc = device->dev_ops.dev_video_on(device, cfg, on);
if (rc)
pr_err("%s:%s: video on failed\n", device->chip_name,
__func__);
else
c->video_on = on;
} else {
c->video_on = false;
list_for_each(pos, &device->client_list) {
node = list_entry(pos, struct msm_dba_client_info,
list);
if (c->video_on) {
video_on = true;
break;
}
}
if (!video_on) {
rc = device->dev_ops.dev_video_on(device, cfg, false);
if (rc) {
pr_err("%s:%s: video off failed\n",
device->chip_name, __func__);
c->video_on = true;
}
}
}
mutex_unlock(&device->dev_mutex);
return rc;
}
int msm_dba_helper_interrupts_enable(void *client, bool on,
u32 event_mask, u32 flags)
{
struct msm_dba_client_info *c = client;
struct msm_dba_device_info *device;
if (!c) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
device = c->dev;
mutex_lock(&device->dev_mutex);
if (on)
c->event_mask = event_mask;
else
c->event_mask = 0;
mutex_unlock(&device->dev_mutex);
return 0;
}
int msm_dba_helper_register_irq(struct msm_dba_device_info *dev,
u32 irq, u32 irq_flags)
{
int rc;
if (!dev) {
pr_err("%s: invalid params\n", __func__);
return -EINVAL;
}
mutex_lock(&dev->dev_mutex);
rc = request_threaded_irq(irq, NULL, msm_dba_helper_irq_handler,
irq_flags, dev->chip_name, dev);
if (rc)
pr_err("%s:%s: Failed to register irq\n", dev->chip_name,
__func__);
mutex_unlock(&dev->dev_mutex);
return rc;
}
int msm_dba_helper_get_caps(void *client, struct msm_dba_capabilities *caps)
{
struct msm_dba_client_info *c = client;
struct msm_dba_device_info *device;
if (!c || !caps) {
pr_err("%s: invalid params\n", __func__);
return -EINVAL;
}
device = c->dev;
mutex_lock(&device->dev_mutex);
memcpy(caps, &device->caps, sizeof(*caps));
mutex_unlock(&device->dev_mutex);
return 0;
}
int msm_dba_register_hdcp_monitor(struct msm_dba_device_info *dev, bool enable)
{
int rc = 0;
if (!dev) {
pr_err("%s: invalid params\n", __func__);
return -EINVAL;
}
if (enable) {
dev->hdcp_wq = alloc_workqueue("hdcp_monitor(%s:%d)", 0, 0,
dev->chip_name,
dev->instance_id);
if (!dev->hdcp_wq) {
pr_err("%s: failed to allocate wq\n", __func__);
rc = -ENOMEM;
goto fail;
}
INIT_WORK(&dev->hdcp_work, msm_dba_helper_hdcp_handler);
dev->hdcp_monitor_on = true;
} else if (!enable && dev->hdcp_wq) {
destroy_workqueue(dev->hdcp_wq);
dev->hdcp_wq = NULL;
dev->hdcp_monitor_on = false;
}
fail:
return rc;
}
int msm_dba_helper_force_reset(void *client, u32 flags)
{
struct msm_dba_client_info *c = client;
struct msm_dba_device_info *device;
int rc = 0;
if (!c) {
pr_err("%s: invalid params\n", __func__);
return -EINVAL;
}
device = c->dev;
mutex_lock(&device->dev_mutex);
msm_dba_helper_issue_cb(device, c, MSM_DBA_CB_PRE_RESET);
if (device->dev_ops.force_reset)
rc = device->dev_ops.force_reset(device, flags);
if (rc)
pr_err("%s: Force reset failed\n", __func__);
msm_dba_helper_issue_cb(device, c, MSM_DBA_CB_POST_RESET);
mutex_unlock(&device->dev_mutex);
return rc;
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <video/msm_dba.h>
#include "msm_dba_internal.h"
struct msm_dba_device_list {
struct msm_dba_device_info *dev;
struct list_head list;
};
static LIST_HEAD(device_list);
static DEFINE_MUTEX(init_mutex);
int msm_dba_add_probed_device(struct msm_dba_device_info *dev)
{
struct msm_dba_device_list *node;
if (!dev) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
mutex_lock(&init_mutex);
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) {
mutex_unlock(&init_mutex);
return -ENOMEM;
}
memset(node, 0x0, sizeof(*node));
node->dev = dev;
list_add(&node->list, &device_list);
pr_debug("%s: Added new device (%s, %d)", __func__, dev->chip_name,
dev->instance_id);
mutex_unlock(&init_mutex);
return 0;
}
int msm_dba_get_probed_device(struct msm_dba_reg_info *reg,
struct msm_dba_device_info **dev)
{
int rc = 0;
struct msm_dba_device_list *node;
struct list_head *position = NULL;
if (!reg || !dev) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
mutex_lock(&init_mutex);
*dev = NULL;
list_for_each(position, &device_list) {
node = list_entry(position, struct msm_dba_device_list, list);
if (!strcmp(reg->chip_name, node->dev->chip_name) &&
reg->instance_id == node->dev->instance_id) {
pr_debug("%s: Found device (%s, %d)\n", __func__,
reg->chip_name,
reg->instance_id);
*dev = node->dev;
break;
}
}
if (!*dev) {
pr_err("%s: Device not found (%s, %d)\n", __func__,
reg->chip_name,
reg->instance_id);
rc = -ENODEV;
}
mutex_unlock(&init_mutex);
return rc;
}
int msm_dba_remove_probed_device(struct msm_dba_device_info *dev)
{
struct msm_dba_device_list *node;
struct list_head *position = NULL;
struct list_head *temp = NULL;
if (!dev) {
pr_err("%s: Invalid params\n", __func__);
return -EINVAL;
}
mutex_lock(&init_mutex);
list_for_each_safe(position, temp, &device_list) {
node = list_entry(position, struct msm_dba_device_list, list);
if (node->dev == dev) {
list_del(&node->list);
pr_debug("%s: Removed device (%s, %d)\n", __func__,
dev->chip_name,
dev->instance_id);
kfree(node);
break;
}
}
mutex_unlock(&init_mutex);
return 0;
}

View file

@ -0,0 +1,317 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MSM_DBA_INTERNAL_H
#define _MSM_DBA_INTERNAL_H
#include <video/msm_dba.h>
struct msm_dba_client_info;
struct msm_dba_device_info;
/**
* struct msm_dba_device_ops - Function pointers to device specific operations
* @dev_power_on: Power on operation called by msm_dba_helper_power_on. Mutex
* protection is handled by the caller.
* @dev_video_on: Video on operation called by msm_dba_helper_video_on. Mutex
* protection is handled by the caller.
* @handle_interrupts: Function pointer called when an interrupt is fired. If
* the bridge driver uses msm_dba_helper_register_irq
* for handling interrupts, irq handler will call
* handle_interrupts to figure out the event mask.
* @unmask_interrupts: Function pointer called by irq handler for unmasking
* interrupts.
* @hdcp_reset: Function pointer to reset the HDCP block. This needs to be valid
* if HDCP monitor is used.
* @hdcp_retry: Function pointer to retry HDCP authentication. This needs to be
* valid if HDCP monitor is used.
* @write_reg: Function pointer to write to device specific register.
* @read_reg: Function pointer to read device specific register.
* @force_reset: Function pointer to force reset the device.
* @dump_debug_info: Function pointer to trigger a dump to dmesg.
*
* The device operation function pointers are used if bridge driver uses helper
* functions in place of some client operations. If used, the helper functions
* will call the device function pointers to perform device specific
* programming.
*/
struct msm_dba_device_ops {
int (*dev_power_on)(struct msm_dba_device_info *dev, bool on);
int (*dev_video_on)(struct msm_dba_device_info *dev,
struct msm_dba_video_cfg *cfg, bool on);
int (*handle_interrupts)(struct msm_dba_device_info *dev, u32 *mask);
int (*unmask_interrupts)(struct msm_dba_device_info *dev, u32 mask);
int (*hdcp_reset)(struct msm_dba_device_info *dev);
int (*hdcp_retry)(struct msm_dba_device_info *dev, u32 flags);
int (*write_reg)(struct msm_dba_device_info *dev, u32 reg, u32 val);
int (*read_reg)(struct msm_dba_device_info *dev, u32 reg, u32 *val);
int (*force_reset)(struct msm_dba_device_info *dev, u32 flags);
int (*dump_debug_info)(struct msm_dba_device_info *dev, u32 flags);
};
/**
* struct msm_dba_device_info - Device specific information
* @chip_name: chip name
* @instance_id: Instance id
* @caps: Capabilities of the bridge chip
* @dev_ops: function pointers to device specific operations
* @client_ops: function pointers to client operations
* @dev_mutex: mutex for protecting device access
* @hdcp_wq: HDCP workqueue for handling failures.
* @client_list: list head for client list
* @reg_fxn: Function pointer called when a client registers with dba driver
* @dereg_fxn: Function pointer called when a client deregisters.
* @power_status: current power status of device
* @video_status: current video status of device
* @audio_status: current audio status of device
* @hdcp_on: hdcp enable status.
* @enc-on: encryption enable status.
* @hdcp_status: hdcp link status.
* @hdcp_monitor_on: hdcp monitor status
* @register_val: debug field used to support read register.
*
* Structure containing device specific information. This structure is allocated
* by the bridge driver. This structure should be unique to each device.
*
*/
struct msm_dba_device_info {
char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN];
u32 instance_id;
struct msm_dba_capabilities caps;
struct msm_dba_device_ops dev_ops;
struct msm_dba_ops client_ops;
struct mutex dev_mutex;
struct workqueue_struct *hdcp_wq;
struct work_struct hdcp_work;
struct list_head client_list;
int (*reg_fxn)(struct msm_dba_client_info *client);
int (*dereg_fxn)(struct msm_dba_client_info *client);
bool power_status;
bool video_status;
bool audio_status;
bool hdcp_on;
bool enc_on;
bool hdcp_status;
bool hdcp_monitor_on;
/* Debug info */
u32 register_val;
};
/**
* struct msm_dba_client_info - Client specific information
* @dev: pointer to device information
* @client_name: client name
* @power_on: client power on status
* @video_on: client video on status
* @audio_on: client audio on status
* @event_mask: client event mask for callbacks.
* @cb: callback function for the client
* @cb_data: callback data pointer.
* @list: list pointer
*
* This structure is used to uniquely identify a client for a bridge chip. The
* pointer to this structure is returned as a handle from
* msm_dba_register_client.
*/
struct msm_dba_client_info {
struct msm_dba_device_info *dev;
char client_name[MSM_DBA_CLIENT_NAME_LEN];
bool power_on;
bool video_on;
bool audio_on;
u32 event_mask;
msm_dba_cb cb;
void *cb_data;
struct list_head list;
};
/**
* msm_dba_add_probed_device() - Add a new device to the probed devices list.
* @info: Pointer to structure containing the device information. This should be
* allocated by the specific bridge driver and kept until
* msm_dba_remove_probed_device() is called.
*
* Once a bridge chip is initialized and probed, it should add its device to the
* existing list of all probed display bridge chips. This list is maintained by
* the MSM DBA driver and is checked whenever there is a client register
* request.
*/
int msm_dba_add_probed_device(struct msm_dba_device_info *info);
/**
* msm_dba_remove_probed_device() - Remove a device from the probed devices list
* @info: Pointer to structure containing the device info. This should be the
* same pointer used for msm_dba_add_probed_device().
*
* Bridge chip driver should call this to remove device from probed list.
*/
int msm_dba_remove_probed_device(struct msm_dba_device_info *info);
/**
* msm_dba_get_probed_device() - Check if a device is present in the device list
* @reg: Pointer to structure containing the chip info received from the client
* driver
* @info: Pointer to the device info pointer that will be returned if the device
* has been found in the device list
*
* When clients of the MSM DBA driver call msm_dba_register_client(), the MSM
* DBA driver will use this function to check if the specific device requested
* by the client has been probed. If probed, function will return a pointer to
* the device information structure.
*/
int msm_dba_get_probed_device(struct msm_dba_reg_info *reg,
struct msm_dba_device_info **info);
/**
* msm_dba_helper_i2c_read() - perform an i2c read transaction
* @client: i2c client pointer
* @addr: i2c slave address
* @reg: register where the data should be read from
* @buf: buffer where the read data is stored.
* @size: bytes to read from slave. buffer should be atleast size bytes.
*
* Helper function to perform a read from an i2c slave. Internally this calls
* i2c_transfer().
*/
int msm_dba_helper_i2c_read(struct i2c_client *client,
u8 addr,
u8 reg,
char *buf,
u32 size);
/**
* msm_dba_helper_i2c_write_buffer() - write buffer to i2c slave.
* @client: i2c client pointer
* @addr: i2c slave address
* @buf: buffer where the data will be read from.
* @size: bytes to write.
*
* Helper function to perform a write to an i2c slave. Internally this calls
* i2c_transfer().
*/
int msm_dba_helper_i2c_write_buffer(struct i2c_client *client,
u8 addr,
u8 *buf,
u32 size);
/**
* msm_dba_helper_i2c_write_byte() - write to a register on an i2c slave.
* @client: i2c client pointer
* @addr: i2c slave address
* @reg: slave register to write to
* @val: data to write.
*
* Helper function to perform a write to an i2c slave. Internally this calls
* i2c_transfer().
*/
int msm_dba_helper_i2c_write_byte(struct i2c_client *client,
u8 addr,
u8 reg,
u8 val);
/**
* msm_dba_helper_power_on() - power on bridge chip
* @client: client handle
* @on: on/off
* @flags: flags
*
* This helper function can be used as power_on() function defined in struct
* msm_dba_ops. Internally, this function does some bookkeeping to figure out
* when to actually power on/off the device. If used, bridge driver should
* provide a dev_power_on to do the device specific power change.
*/
int msm_dba_helper_power_on(void *client, bool on, u32 flags);
/**
* msm_dba_helper_video_on() - video on bridge chip
* @client: client handle
* @on: on/off
* @flags: flags
*
* This helper function can be used as video_on() function defined in struct
* msm_dba_ops. Internally, this function does some bookkeeping to figure out
* when to actually video on/off the device. If used, bridge driver should
* provide a dev_video_on to do the device specific video change.
*/
int msm_dba_helper_video_on(void *client, bool on,
struct msm_dba_video_cfg *cfg, u32 flags);
/**
* msm_dba_helper_interrupts_enable() - manage interrupt callbacks
* @client: client handle
* @on: on/off
* @events_mask: events on which callbacks are required.
* @flags: flags
*
* This helper function provides the functionality needed for interrupts_enable
* function pointer in struct msm_dba_ops.
*/
int msm_dba_helper_interrupts_enable(void *client, bool on,
u32 events_mask, u32 flags);
/**
* msm_dba_helper_get_caps() - return device capabilities
* @client: client handle
* @flags: flags
*
* Helper function to replace get_caps function pointer in struct msm_dba_ops
* structure.
*/
int msm_dba_helper_get_caps(void *client, struct msm_dba_capabilities *caps);
/**
* msm_dba_helper_register_irq() - register irq and handle interrupts.
* @dev: pointer to device structure
* @irq: irq number
* @irq_flags: irq_flags.
*
* Helper function register an irq and handling interrupts. This will attach a
* threaded interrupt handler to the irq provided as input. When the irq
* handler is triggered, handler will call handle_interrupts in the device
* specific functions pointers so that bridge driver can parse the interrupt
* status registers and return the event mask. IRQ handler will use this event
* mask to provide callbacks to the clients. Once the callbacks are done,
* handler will call unmask_interrupts() before returning,
*/
int msm_dba_helper_register_irq(struct msm_dba_device_info *dev,
u32 irq, u32 irq_flags);
/**
* msm_dba_register_hdcp_monitor() - kicks off monitoring for hdcp failures
* @dev: pointer to device structure.
* @enable: enable/disable
*
* Helper function to enable HDCP monitoring. This should be called only if irq
* is handled through msm dba helper functions.
*/
int msm_dba_register_hdcp_monitor(struct msm_dba_device_info *dev, bool enable);
/**
* msm_dba_helper_sysfs_init() - create sysfs attributes for debugging
* @dev: pointer to struct device structure.
*
*/
int msm_dba_helper_sysfs_init(struct device *dev);
/**
* msm_dba_helper_force_reset() - force reset bridge chip
* @client: client handle
* @flags: flags
*
* Helper function to replace force_reset function pointer in struct msm_dba_ops
* structure. Driver should set dev_ops.force_reset to a valid function.
*/
int msm_dba_helper_force_reset(void *client, u32 flags);
#endif /* _MSM_DBA_INTERNAL_H */

503
include/video/msm_dba.h Normal file
View file

@ -0,0 +1,503 @@
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MSM_DBA_H
#define _MSM_DBA_H
#include <linux/types.h>
#include <linux/bitops.h>
#define MSM_DBA_CHIP_NAME_MAX_LEN 20
#define MSM_DBA_CLIENT_NAME_LEN 20
#define MSM_DBA_DEFER_PROPERTY_FLAG 0x1
#define MSM_DBA_ASYNC_FLAG 0x2
/**
* enum msm_dba_callback_event - event types for callback notification
* @MSM_DBA_CB_REMOTE_INT: Event associated with remote devices on an interface
* that supports a bi-directional control channel.
* @MSM_DBA_CB_HDCP_LINK_AUTHENTICATED: Authentication session is successful.
* The link is authenticated and encryption
* can be enabled if not enabled already.
* @MSM_DBA_CB_HDCP_LINK_UNAUTHENTICATED: A previously authenticated link has
* failed. The content on the interface
* is no longer secure.
* @MSM_DBA_CB_HPD_CONNECT: Detected a cable connect event.
* @MSM_DBA_CB_HPD_DISCONNECT: Detected a cable disconnect event.
* @MSM_DBA_CB_VIDEO_FAILURE: Detected a failure with respect to video data on
* the interface. This is a generic failure and
* client should request a debug dump to debug the
* issue. Client can also attempt a reset to recover
* the device.
* @MSM_DBA_CB_AUDIO_FAILURE: Detected a failure with respect to audio data on
* the interface. This is a generic failure and
* client should request a debug dump. Client can
* also attempt a reset to recover the device.
* @MSM_DBA_CB_CEC_WRITE_SUCCESS: The asynchronous CEC write request is
* successful.
* @MSM_DBA_CB_CEC_WRITE_FAIL: The asynchronous CEC write request failed.
* @MSM_DBA_CB_CEC_READ_PENDING: There is a pending CEC read message.
* @MSM_DBA_CB_PRE_RESET: This callback is called just before the device is
* being reset.
* @MSM_DBA_CB_POST_RESET: This callback is called after device reset is
* complete and the driver has applied back all the
* properties.
*
* Clients for this driver can register for receiving callbacks for specific
* events. This enum defines the type of events supported by the driver. An
* event mask is typically used to denote multiple events.
*/
enum msm_dba_callback_event {
MSM_DBA_CB_REMOTE_INT = BIT(0),
MSM_DBA_CB_HDCP_LINK_AUTHENTICATED = BIT(1),
MSM_DBA_CB_HDCP_LINK_UNAUTHENTICATED = BIT(2),
MSM_DBA_CB_HPD_CONNECT = BIT(3),
MSM_DBA_CB_HPD_DISCONNECT = BIT(4),
MSM_DBA_CB_VIDEO_FAILURE = BIT(5),
MSM_DBA_CB_AUDIO_FAILURE = BIT(6),
MSM_DBA_CB_CEC_WRITE_SUCCESS = BIT(7),
MSM_DBA_CB_CEC_WRITE_FAIL = BIT(8),
MSM_DBA_CB_CEC_READ_PENDING = BIT(9),
MSM_DBA_CB_PRE_RESET = BIT(10),
MSM_DBA_CB_POST_RESET = BIT(11),
};
/**
* enum msm_dba_audio_interface_type - audio interface type
* @MSM_DBA_AUDIO_I2S_INTERFACE: I2S interface for audio
* @MSM_DBA_AUDIO_SPDIF_INTERFACE: SPDIF interface for audio
*/
enum msm_dba_audio_interface_type {
MSM_DBA_AUDIO_I2S_INTERFACE = BIT(0),
MSM_DBA_AUDIO_SPDIF_INTERFACE = BIT(1),
};
/**
* enum msm_dba_audio_format_type - audio format type
* @MSM_DBA_AUDIO_FMT_UNCOMPRESSED_LPCM: uncompressed format
* @MSM_DBA_AUDIO_FMT_COMPRESSED: compressed formats
*/
enum msm_dba_audio_format_type {
MSM_DBA_AUDIO_FMT_UNCOMPRESSED_LPCM = BIT(0),
MSM_DBA_AUDIO_FMT_COMPRESSED = BIT(1),
};
/**
* enum msm_dba_audio_sampling_rates_type - audio sampling rates
* @MSM_DBA_AUDIO_32KHZ: 32KHz sampling rate
* @MSM_DBA_AUDIO_44P1KHZ: 44.1KHz sampling rate
* @MSM_DBA_AUDIO_48KHZ: 48KHz sampling rate
* @MSM_DBA_AUDIO_96KHZ: 96KHz sampling rate
* @MSM_DBA_AUDIO_192KHZ: 192KHz sampling rate
*/
enum msm_dba_audio_sampling_rates_type {
MSM_DBA_AUDIO_32KHZ = BIT(0),
MSM_DBA_AUDIO_44P1KHZ = BIT(1),
MSM_DBA_AUDIO_48KHZ = BIT(2),
MSM_DBA_AUDIO_96KHZ = BIT(3),
MSM_DBA_AUDIO_192KHZ = BIT(4),
};
/**
* enum msm_dba_audio_word_bit_depth - audio word size
* @MSM_DBA_AUDIO_WORD_16BIT: 16 bits per word
* @MSM_DBA_AUDIO_WORD_24BIT: 24 bits per word
* @MSM_DBA_AUDIO_WORD_32BIT: 32 bits per word
*/
enum msm_dba_audio_word_bit_depth {
MSM_DBA_AUDIO_WORD_16BIT = BIT(0),
MSM_DBA_AUDIO_WORD_24BIT = BIT(1),
MSM_DBA_AUDIO_WORD_32BIT = BIT(2),
};
/**
* enum msm_dba_audio_channel_count - audio channel count
* @MSM_DBA_AUDIO_CHANNEL_2: 2 channel audio
* @MSM_DBA_AUDIO_CHANNEL_4: 4 channel audio
* @MSM_DBA_AUDIO_CHANNEL_8: 8 channel audio
*/
enum msm_dba_audio_channel_count {
MSM_DBA_AUDIO_CHANNEL_2 = BIT(0),
MSM_DBA_AUDIO_CHANNEL_4 = BIT(1),
MSM_DBA_AUDIO_CHANNEL_8 = BIT(2),
};
/**
* enum msm_dba_audio_i2s_format - i2s audio data format
* @MSM_DBA_AUDIO_I2S_FMT_STANDARD: Standard format
* @MSM_DBA_AUDIO_I2S_FMT_RIGHT_JUSTIFIED: i2s data is right justified
* @MSM_DBA_AUDIO_I2S_FMT_LEFT_JUSTIFIED: i2s data is left justified
* @MSM_DBA_AUDIO_I2S_FMT_AES3_DIRECT: AES signal format
*/
enum msm_dba_audio_i2s_format {
MSM_DBA_AUDIO_I2S_FMT_STANDARD = 0,
MSM_DBA_AUDIO_I2S_FMT_RIGHT_JUSTIFIED,
MSM_DBA_AUDIO_I2S_FMT_LEFT_JUSTIFIED,
MSM_DBA_AUDIO_I2S_FMT_AES3_DIRECT,
MSM_DBA_AUDIO_I2S_FMT_MAX,
};
enum msm_dba_video_aspect_ratio {
MSM_DBA_AR_UNKNOWN = 0,
MSM_DBA_AR_4_3,
MSM_DBA_AR_5_4,
MSM_DBA_AR_16_9,
MSM_DBA_AR_16_10,
MSM_DBA_AR_64_27,
MSM_DBA_AR_256_135,
MSM_DBA_AR_MAX
};
/**
* typedef *msm_dba_cb() - Prototype for callback function
* @data: Pointer to user data provided with register API
* @event: Event type associated with callback. This can be a bitmask.
*/
typedef void (*msm_dba_cb)(void *data, enum msm_dba_callback_event event);
/**
* struct msm_dba_reg_info - Client information used with register API
* @client_name: Name of the client for debug purposes
* @chip_name: Bridge chip ID
* @instance_id: Instance ID of the bridge chip in case of multiple instances
* @cb: callback function called in case of events.
* @cb_data: pointer to a data structure that will be returned with callback
*
* msm_dba_reg_info structure will be used to provide information during
* registering with driver. This structure will contain the information required
* to identify the specific bridge chip the client wants to use.
*
* Client should also specify the callback function which needs to be called in
* case of events. There is an optional data field which is a pointer that will
* be returned as one of arguments in the callback function. This data field can
* be NULL if client does not wish to use it.
*/
struct msm_dba_reg_info {
char client_name[MSM_DBA_CLIENT_NAME_LEN];
char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN];
u32 instance_id;
msm_dba_cb cb;
void *cb_data;
};
/**
* struct msm_dba_video_caps_info - video capabilities of the bridge chip
* @hdcp_support: if hdcp is supported
* @edid_support: if reading edid from sink is supported
* @data_lanes_lp_support: if low power mode is supported on data lanes
* @clock_lanes_lp_support: If low power mode is supported on clock lanes
* @max_pclk_khz: maximum pixel clock supported
* @num_of_input_lanes: Number of input data lanes supported by the bridge chip
*/
struct msm_dba_video_caps_info {
bool hdcp_support;
bool edid_support;
bool data_lanes_lp_support;
bool clock_lanes_lp_support;
u32 max_pclk_khz;
u32 num_of_input_lanes;
};
/**
* struct msm_dba_audio_caps_info - audio capabilities of the bridge chip
* @audio_support: if audio is supported
* @audio_rates: audio sampling rates supported
* @audio_fmts: audio formats supported
*/
struct msm_dba_audio_caps_info {
u32 audio_support;
u32 audio_rates;
u32 audio_fmts;
};
/**
* struct msm_dba_capabilities - general capabilities of the bridge chip
* @vid_caps: video capabilities
* @aud_caps: audio capabilities
* @av_mute_support: av mute support in bridge chip
* @deferred_commit_support: support for deferred commit
*/
struct msm_dba_capabilities {
struct msm_dba_video_caps_info vid_caps;
struct msm_dba_audio_caps_info aud_caps;
bool av_mute_support;
bool deferred_commit_support;
};
/**
* struct msm_dba_audio_cfg - Structure for audio configuration
* @interface: Specifies audio interface type. Client should check the
* capabilities for the interfaces supported by the bridge.
* @format: Compressed vs Uncompressed formats.
* @channels: Number of channels.
* @i2s_fmt: I2S data packing format. This is valid only if interface is I2S.
*/
struct msm_dba_audio_cfg {
enum msm_dba_audio_interface_type interface;
enum msm_dba_audio_format_type format;
enum msm_dba_audio_channel_count channels;
enum msm_dba_audio_i2s_format i2s_fmt;
};
/**
* struct msm_dba_video_cfg - video configuration data
* @h_active: active width of the video signal
* @h_front_porch: horizontal front porch in pixels
* @h_pulse_width: pulse width of hsync in pixels
* @h_back_porch: horizontal back porch in pixels
* @h_polarity: polarity of hsync signal
* @v_active: active height of the video signal
* @v_front_porch: vertical front porch in lines
* @v_pulse_width: pulse width of vsync in lines
* @v_back_porch: vertical back porch in lines
* @v_polarity: polarity of vsync signal
* @pclk_khz: pixel clock in KHz
* @interlaced: if video is interlaced
* @vic: video indetification code
* @hdmi_mode: hdmi or dvi mode for the sink
* @ar: aspect ratio of the signal
* @num_of_input_lanes: number of input lanes in case of DSI/LVDS
*/
struct msm_dba_video_cfg {
u32 h_active;
u32 h_front_porch;
u32 h_pulse_width;
u32 h_back_porch;
bool h_polarity;
u32 v_active;
u32 v_front_porch;
u32 v_pulse_width;
u32 v_back_porch;
bool v_polarity;
u32 pclk_khz;
bool interlaced;
u32 vic;
bool hdmi_mode;
enum msm_dba_video_aspect_ratio ar;
u32 num_of_input_lanes;
};
/**
* struct msm_dba_ops- operation supported by bridge chip
* @get_caps: returns the bridge chip capabilities
* DEFER and ASYNC flags are not supported.
* @power_on: powers on/off the bridge chip. This usually involves turning on
* the power regulators and bringing the chip out of reset. Chip
* should be capable of raising interrupts at this point.
* DEFER and ASYNC flags are supported.
* @video_on: turn on/off video stream. This function also requires the video
* timing information that might be needed for programming the bridge
* chip.
* DEFER flag is supported.
* ASYNC flag is not supported.
* @audio_on: turn on/off audio stream.
* DEFER flag is supported.
* ASYNC flag is not supported.
* @configure_audio: setup audio configuration
* DEFER flag is supported.
* ASYNC flag is not supported.
* @av_mute: controls av mute functionalities if supported. AV mute is different
* from audio_on and video_on where in even though the actual data is
* sent, mute is specified through control packets.
* DEFER flag is supported.
* ASYNC flag is not supported.
* @interupts_enable: enables interrupts to get event callbacks. Clients need
* to specify an event mask of the events they are
* interested in. If a client provides an event as part of
* the mask, it will receive the interrupt regardless of the
* client modifying the property.
* DEFER flag is supported.
* ASYNC flag is not supported.
* @hdcp_enable: enable/disable hdcp. If HDCP is enabled, this function will
* start a new authentication session. There is a separate
* argument for enabling encryption. Encryption can be enabled any
* time after HDCP has been fully authenticated. This function
* will support an asynchronous mode where calling this function
* will kick off HDCP and return to the caller. Caller has to wait
* for MSM_DBA_CB_HDCP_SUCCESS callback to ensure link is
* authenticated.
* DEFER flag is not supported.
* ASYNC flag is supported.
* @hdcp_get_ksv_list_size: returns the KSV list size. In case of a simple sink
* the size will be 1. In case of a repeater, this can
* be more than one.
* DEFER and ASYNC flags are not supported.
* @hdcp_get_ksv_list: return the KSV list. Client can query the KSV information
* from the bridge. Client should call
* hdcp_get_ksv_list_size first and then allocate 40*size
* bytes to hold all the KSVs.
* DEFER and ASYNC flags are not supported.
* @hdmi_cec_write: perform a CEC write. For bridges with HDMI as output
* interface, this function allows clients to send a CEC
* message. Client should pack the data according to the CEC
* specification and provide the final buffer. Since CEC writes
* can take longer time to ascertaining if they are successful,
* this function supports the ASYNC flag. Driver will return
* either MSM_DBA_CB_CEC_WRITE_SUCCESS or
* MSM_DBA_CB_CEC_WRITE_FAIL callbacks.
* DEFER is not supported.
* ASYNC flag is supported.
* @hdmi_cec_read: get a pending CEC read message. In case of an incoming CEC
* message, driver will return MSM_DBA_CB_CEC_READ_PENDING
* callback. On getting this event callback, client should call
* hdmi_cec_read to get the message. The buffer should at least
* be 15 bytes or more. Client should read the CEC message from
* a thread different from the callback.
* DEFER and ASYNC flags are not supported.
* @get_edid_size: returns size of the edid.
* DEFER and ASYNC flags are not supported.
* @get_raw_edid: returns raw edid data.
* DEFER and ASYNC flags are not supported.
* @enable_remote_comm: enable/disable remote communication. Some interfaces
* like FPDLINK III support a bi-directional control
* channel that could be used to send control data using an
* I2C or SPI protocol. This Function will enable this
* control channel if supported.
* DEFER and ASYNC flags are not supported.
* @add_remote_device: add slaves on remote side for enabling communication. For
* interfaces that support bi directional control channel,
* this function allows clients to specify slave IDs of
* devices on remote bus. Messages addressed to these IDs
* will be trapped by the bridge chip and put on the remote
* bus.
* DEFER and ASYNC flags are not supported.
* @commit_deferred_props: commits deferred properties
* DEFER and ASYNC flags are not supported.
* @force_reset: reset the device forcefully. In case the device goes into a bad
* state, a client can force reset to try and recover the device.
* The reset will be applied in spite of different configurations
* from other clients. Driver will apply all the properties that
* have been applied so far after the reset is complete. In case
* of multiple clients, driver will issue a reset callback.
* @dump_debug_info: dumps debug information to dmesg.
*
* The msm_dba_ops structure represents a set of operations that can be
* supported by each bridge chip. Depending on the functionality supported by a
* specific bridge chip, some of the operations need not be supported. For
* example if a bridge chip does not support reading EDID from a sink device,
* get_edid_size and get_raw_edid can be NULL.
*
* Deferring properties: The deferred flag allows us to address any quirks with
* respect to specific bridge chips. If there is a need for some properties to
* be committed together, turning on video and audio at the same time, the
* deferred flag can be used. Properties that are set using a DEFER flag will
* not be committed to hardware until commit_deferred_props() function is
* called.
*
*/
struct msm_dba_ops {
int (*get_caps)(void *client,
struct msm_dba_capabilities *caps);
int (*power_on)(void *client,
bool on,
u32 flags);
int (*video_on)(void *client,
bool on,
struct msm_dba_video_cfg *cfg,
u32 flags);
int (*audio_on)(void *client,
bool on,
u32 flags);
int (*configure_audio)(void *client,
struct msm_dba_audio_cfg *cfg,
u32 flags);
int (*av_mute)(void *client,
bool video_mute,
bool audio_mute,
u32 flags);
int (*interrupts_enable)(void *client,
bool on,
u32 event_mask,
u32 flags);
int (*hdcp_enable)(void *client,
bool hdcp_on,
bool enc_on,
u32 flags);
int (*hdcp_get_ksv_list_size)(void *client,
u32 *count,
u32 flags);
int (*hdcp_get_ksv_list)(void *client,
u32 count,
char *buf,
u32 flags);
int (*hdmi_cec_write)(void *client,
u32 size,
char *buf,
u32 flags);
int (*hdmi_cec_read)(void *client,
u32 *size,
char *buf,
u32 flags);
int (*get_edid_size)(void *client,
u32 *size,
u32 flags);
int (*get_raw_edid)(void *client,
u32 size,
char *buf,
u32 flags);
int (*enable_remote_comm)(void *client,
bool on,
u32 flags);
int (*add_remote_device)(void *client,
u32 *slave_ids,
u32 count,
u32 flags);
int (*commit_deferred_props)(void *client,
u32 flags);
int (*force_reset)(void *client, u32 flags);
int (*dump_debug_info)(void *client, u32 flags);
};
/**
* msm_dba_register_client() - Allows a client to register with the driver.
* @info: Client information along with the bridge chip id the client wishes to
* program.
* @ops: Function pointers to bridge chip operations. Some function pointers can
* be NULL depending on the functionalities supported by bridge chip.
*
* The register API supports multiple clients to register for the same bridge
* chip. If Successful, this will return a pointer that should be used as a
* handle for all subsequent function calls.
*/
void *msm_dba_register_client(struct msm_dba_reg_info *info,
struct msm_dba_ops *ops);
/**
* msm_dba_deregister_client() - Allows client to de-register with the driver.
* @client: client handle returned by register API.
*
* This function will release all the resources used by a particular client. If
* it is the only client using the bridge chip, the bridge chip will be powered
* down and put into reset.
*/
int msm_dba_deregister_client(void *client);
#endif /* _MSM_DBA_H */