M7350/kernel/drivers/video/msm/msm_dba/msm_dba_helpers.c
2024-09-09 08:57:42 +00:00

446 lines
9.6 KiB
C

/*
* 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;
}