Files
kernel_common_drivers/drivers/dvb_ci/aml_ci.c
T
hongyu.chen b7181587ad dvbci: fix dependent on demux driver. [1/1]
PD#SWPL-119805

Problem:
dependent on demux driver.

Solution:
dependent on dvb core.

Verify:
no verify.

Change-Id: Idf14192e65db6ee026c5b41fc6e599a1590e99ea
Signed-off-by: hongyu.chen <hongyu.chen@amlogic.com>
2023-04-19 06:11:14 -07:00

500 lines
11 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <linux/of.h>
#include <dvbdev.h>
#include "aml_ci.h"
#include "aml_ci_bus.h"
MODULE_PARM_DESC(aml_ci_debug, "\n\t\t dvb ci debug");
static int aml_ci_debug = 1;
module_param(aml_ci_debug, int, 0444);
#define pr_dbg(args...)\
do {\
if (aml_ci_debug)\
pr_err("aml_ci: " args);\
} while (0)
#define pr_error(fmt, args...) pr_err("DVBCI: " fmt, ## args)
/**\brief aml_ci_mem_read:mem read from cam
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \param addr: read addr
* \return
* - read value:ok
* - -EINVAL : error
*/
static int aml_ci_mem_read(struct dvb_ca_en50221_cimcu *en50221, int slot, int addr)
{
struct aml_ci *ci = en50221->data;
if (slot != 0) {
pr_error("slot !=0 %s :%d\r\n", __func__, slot);
return -EINVAL;
}
if (ci->ci_mem_read)
return ci->ci_mem_read(ci, slot, addr);
pr_error("ci_mem_read is null %s\r\n", __func__);
return -EINVAL;
}
/**\brief aml_ci_mem_write:mem write to cam
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \param addr: write addr
* \param addr: write value
* \return
* - 0:ok
* - -EINVAL : error
*/
static int aml_ci_mem_write(struct dvb_ca_en50221_cimcu *en50221,
int slot, int addr, u8 data)
{
struct aml_ci *ci = en50221->data;
if (slot != 0) {
pr_error("slot not 0 %s :%d\r\n", __func__, slot);
return -EINVAL;
}
if (ci->ci_mem_write)
return ci->ci_mem_write(ci, slot, addr, data);
pr_error("ci_mem_write is null %s\r\n", __func__);
return -EINVAL;
}
/**\brief aml_ci_io_read:io read from cam
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \param addr: read addr
* \return
* - read value:ok
* - -EINVAL : error
*/
static int aml_ci_io_read(struct dvb_ca_en50221_cimcu *en50221, int slot, u8 addr)
{
struct aml_ci *ci = en50221->data;
if (slot != 0) {
pr_error("slot !=0 %s :%d\r\n", __func__, slot);
return -EINVAL;
}
if (ci->ci_io_read)
return ci->ci_io_read(ci, slot, addr);
pr_error("ci_io_read is null %s\r\n", __func__);
return -EINVAL;
}
/**\brief aml_ci_io_write:io write to cam
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \param addr: write addr
* \param addr: write value
* \return
* - 0:ok
* - -EINVAL : error
*/
static int aml_ci_io_write(struct dvb_ca_en50221_cimcu *en50221,
int slot, u8 addr, u8 data)
{
struct aml_ci *ci = en50221->data;
if (slot != 0) {
pr_error("slot !=0 %s :%d\r\n", __func__, slot);
return -EINVAL;
}
if (ci->ci_mem_write)
return ci->ci_io_write(ci, slot, addr, data);
pr_error("ci_io_write is null %s\r\n", __func__);
return -EINVAL;
}
/**\brief aml_ci_slot_reset:reset slot
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \return
* - 0:ok
* - -EINVAL : error
*/
static int aml_ci_slot_reset(struct dvb_ca_en50221_cimcu *en50221, int slot)
{
struct aml_ci *ci = en50221->data;
pr_dbg("Slot(%d): Slot RESET\n", slot);
if (ci->ci_slot_reset) {
ci->ci_slot_reset(ci, slot);
} else {
pr_error("ci_slot_reset is null %s\r\n", __func__);
return -EINVAL;
}
return 0;
}
/**\brief aml_ci_slot_shutdown:show slot
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \return
* - 0:ok
* - -EINVAL : error
*/
static int aml_ci_slot_shutdown(struct dvb_ca_en50221_cimcu *en50221, int slot)
{
struct aml_ci *ci = en50221->data;
pr_dbg("Slot(%d): Slot shutdown\n", slot);
if (ci->ci_slot_shutdown) {
ci->ci_slot_shutdown(ci, slot);
} else {
pr_error("ci_slot_shutdown is null %s\r\n", __func__);
return -EINVAL;
}
return 0;
}
/**\brief aml_ci_ts_control:control slot ts
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \return
* - 0:ok
* - -EINVAL : error
*/
static int aml_ci_ts_control(struct dvb_ca_en50221_cimcu *en50221, int slot)
{
struct aml_ci *ci = en50221->data;
pr_dbg("Slot(%d): TS control\n", slot);
if (ci->ci_slot_ts_enable) {
ci->ci_slot_ts_enable(ci, slot);
} else {
pr_error("%s is null\r\n", __func__);
return -EINVAL;
}
return 0;
}
/**\brief aml_ci_slot_status:get slot status
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \param open: no used
* \return
* - cam status
* - -EINVAL : error
*/
static int aml_ci_slot_status(struct dvb_ca_en50221_cimcu *en50221,
int slot, int open)
{
struct aml_ci *ci = en50221->data;
//pr_dbg("Slot(%d): Poll Slot status\n", slot);
if (ci->ci_poll_slot_status)
return ci->ci_poll_slot_status(ci, slot, open);
return 0;
}
/**\brief aml_ci_slot_status:get slot status
* \param en50221: en50221 obj,used this data to get dvb_ci obj
* \param slot: slot index
* \param open: no used
* \return
* - cam status
* - -EINVAL : error
*/
static int aml_ci_slot_wakeup(struct dvb_ca_en50221_cimcu *en50221,
int slot)
{
struct aml_ci *ci = en50221->data;
if (ci->ci_get_slot_wakeup)
return ci->ci_get_slot_wakeup(ci, slot);
return 1;
}
/**\brief get ci config from dts
* \param np: device node
* \return
* - 0 成功
* - 其他值 :
*/
static int aml_ci_get_config_from_dts(struct platform_device *pdev,
struct aml_ci *ci)
{
char buf[32];
int ret = 0;
int value;
snprintf(buf, sizeof(buf), "%s", "io_type");
ret = of_property_read_u32(pdev->dev.of_node, buf, &value);
if (!ret) {
pr_dbg("%s: 0x%x\n", buf, value);
ci->io_type = value;
}
snprintf(buf, sizeof(buf), "%s", "raw_mode");
ret = of_property_read_u32(pdev->dev.of_node, buf, &value);
if (!ret) {
pr_dbg("%s: 0x%x\n", buf, value);
ci->raw_mode = value;
}
return 0;
}
/**\brief aml_ci_init:ci dev init
* \param pdev: platform_device device node,used to get dts info
* \param dvb: aml_dvb obj,used to get dvb_adapter for en0211 to use
* \param cip: ci_dev pp
* \return
* - 0 成功
* - 其他值 :
*/
int aml_ci_init(struct platform_device *pdev,
struct dvb_adapter *dvb_adapter, struct aml_ci **cip)
{
struct aml_ci *ci = NULL;
int ca_flags = 0, result;
ci = kzalloc(sizeof(*ci), GFP_KERNEL);
if (!ci) {
pr_error("Out of memory!, exiting ..\n");
result = -ENOMEM;
goto err;
}
ci->id = 0;
aml_ci_get_config_from_dts(pdev, ci);
//ci->priv = dvb;
/* register CA interface */
{
ca_flags = ~DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE;
/* register CA interface */
ci->en50221_cimcu.owner = THIS_MODULE;
ci->en50221_cimcu.read_attribute_mem = aml_ci_mem_read;
ci->en50221_cimcu.write_attribute_mem = aml_ci_mem_write;
ci->en50221_cimcu.read_cam_control = aml_ci_io_read;
ci->en50221_cimcu.write_cam_control = aml_ci_io_write;
ci->en50221_cimcu.slot_reset = aml_ci_slot_reset;
ci->en50221_cimcu.slot_shutdown = aml_ci_slot_shutdown;
ci->en50221_cimcu.slot_ts_enable = aml_ci_ts_control;
ci->en50221_cimcu.poll_slot_status = aml_ci_slot_status;
ci->en50221_cimcu.get_slot_wakeup = aml_ci_slot_wakeup;
ci->en50221_cimcu.data = ci;
if (ci->raw_mode == 0) {
pr_dbg("Registering EN50221 device\n");
result = dvb_ca_en50221_cimcu_init(dvb_adapter,
&ci->en50221_cimcu, ca_flags, 1);
if (result != 0) {
pr_error("EN50221_cimcu: Initialization failed <%d>\n",
result);
goto err;
}
}
}
*cip = ci;
pr_dbg("Registered EN50221 device\n");
if (ci->io_type == AML_DVB_IO_TYPE_CIBUS) {
ci->ci_init = aml_ci_bus_init;
ci->ci_exit = aml_ci_bus_exit;
} else {
/* no io dev init,is error */
pr_dbg("unknown io type, please check io_type in dts file\r\n");
}
if (ci->ci_init)
result = ci->ci_init(pdev, ci);
return result;
err:
kfree(ci);
return result;
}
void aml_ci_exit(struct aml_ci *ci)
{
pr_dbg("Unregistering EN50221 device\n");
if (ci) {
if (ci->raw_mode == 0)
dvb_ca_en50221_cimcu_release(&ci->en50221_cimcu);
if (ci->ci_exit)
ci->ci_exit(ci);
kfree(ci);
}
}
static struct aml_ci *ci_dev;
static ssize_t ts_show(struct class *class,
struct class_attribute *attr, char *buf)
{
int ret;
ret = sprintf(buf, "ts%d\n", 1);
return ret;
}
static CLASS_ATTR_RO(ts);
static struct attribute *aml_ci_attrs[] = {
&class_attr_ts.attr,
NULL
};
ATTRIBUTE_GROUPS(aml_ci);
static int aml_ci_register_class(struct aml_ci *ci)
{
#define CLASS_NAME_LEN 48
int ret;
struct class *clp;
clp = &ci->class;
clp->name = kzalloc(CLASS_NAME_LEN, GFP_KERNEL);
if (!clp->name)
return -ENOMEM;
snprintf((char *)clp->name, CLASS_NAME_LEN, "amlci-%d", ci->id);
clp->owner = THIS_MODULE;
clp->class_groups = aml_ci_groups;
ret = class_register(clp);
if (ret)
kfree(clp->name);
return 0;
}
static int aml_ci_unregister_class(struct aml_ci *ci)
{
class_unregister(&ci->class);
kfree(ci->class.name);
return 0;
}
extern struct dvb_adapter *aml_dvb_get_adapter(struct device *dev);
static int aml_ci_probe(struct platform_device *pdev)
{
struct dvb_adapter *dvb_adapter = NULL;
int err = 0;
dvb_adapter = aml_dvb_get_adapter(&pdev->dev);
pr_dbg("---Amlogic CI Init---[%p]\n", dvb_adapter);
err = aml_ci_init(pdev, dvb_adapter, &ci_dev);
if (err < 0)
return err;
if (ci_dev->io_type == AML_DVB_IO_TYPE_CIBUS) {
pr_dbg("*********ci bus aml_ci_bus_mod_init---\n");
aml_ci_bus_mod_init();
}
platform_set_drvdata(pdev, ci_dev);
aml_ci_register_class(ci_dev);
return 0;
}
static int aml_ci_remove(struct platform_device *pdev)
{
aml_ci_unregister_class(ci_dev);
platform_set_drvdata(pdev, NULL);
if (ci_dev->io_type == AML_DVB_IO_TYPE_CIBUS) {
aml_ci_bus_mod_exit();
aml_ci_bus_exit(ci_dev);
} else {
pr_dbg("---Amlogic CI remove unknown io type---\n");
}
aml_ci_exit(ci_dev);
return 0;
}
static int aml_ci_suspend(struct platform_device *pdev, pm_message_t state)
{
pr_dbg("Amlogic CI Suspend!\n");
if (ci_dev->io_type == AML_DVB_IO_TYPE_CIBUS)
aml_ci_bus_exit(ci_dev);
else
pr_dbg("---Amlogic CI remove unknown io type---\n");
return 0;
}
static int aml_ci_resume(struct platform_device *pdev)
{
int err = 0;
pr_dbg("Amlogic CI Resume!\n");
if (ci_dev->io_type == AML_DVB_IO_TYPE_CIBUS)
aml_ci_bus_init(pdev, ci_dev);
else
pr_dbg("---Amlogic CI remove unknown io type---\n");
return err;
}
static const struct of_device_id dvbci_dev_dt_match[] = {
{
.compatible = "amlogic, dvbci",
},
{},
};
static struct platform_driver aml_ci_driver = {
.probe = aml_ci_probe,
.remove = aml_ci_remove,
.suspend = aml_ci_suspend,
.resume = aml_ci_resume,
.driver = {
.name = "dvbci",
.of_match_table = dvbci_dev_dt_match,
.owner = THIS_MODULE,
}
};
static int aml_ci_mod_init(void)
{
pr_dbg("Amlogic CI mode init\n");
return platform_driver_register(&aml_ci_driver);
}
static void aml_ci_mod_exit(void)
{
pr_dbg("Amlogic CI mode Exit\n");
platform_driver_unregister(&aml_ci_driver);
}
module_init(aml_ci_mod_init);
module_exit(aml_ci_mod_exit);
MODULE_LICENSE("GPL");