18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ddbridge-ci.c: Digital Devices bridge CI (DuoFlex, CI Bridge) support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2017 Digital Devices GmbH
68c2ecf20Sopenharmony_ci *                         Marcus Metzler <mocm@metzlerbros.de>
78c2ecf20Sopenharmony_ci *                         Ralph Metzler <rjkm@metzlerbros.de>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License
118c2ecf20Sopenharmony_ci * version 2 only, as published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168c2ecf20Sopenharmony_ci * GNU General Public License for more details.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "ddbridge.h"
208c2ecf20Sopenharmony_ci#include "ddbridge-regs.h"
218c2ecf20Sopenharmony_ci#include "ddbridge-ci.h"
228c2ecf20Sopenharmony_ci#include "ddbridge-io.h"
238c2ecf20Sopenharmony_ci#include "ddbridge-i2c.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "cxd2099.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* Octopus CI internal CI interface */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int wait_ci_ready(struct ddb_ci *ci)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	u32 count = 10;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ndelay(500);
348c2ecf20Sopenharmony_ci	do {
358c2ecf20Sopenharmony_ci		if (ddbreadl(ci->port->dev,
368c2ecf20Sopenharmony_ci			     CI_CONTROL(ci->nr)) & CI_READY)
378c2ecf20Sopenharmony_ci			break;
388c2ecf20Sopenharmony_ci		usleep_range(1, 2);
398c2ecf20Sopenharmony_ci		if ((--count) == 0)
408c2ecf20Sopenharmony_ci			return -1;
418c2ecf20Sopenharmony_ci	} while (1);
428c2ecf20Sopenharmony_ci	return 0;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int read_attribute_mem(struct dvb_ca_en50221 *ca,
468c2ecf20Sopenharmony_ci			      int slot, int address)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
498c2ecf20Sopenharmony_ci	u32 val, off = (address >> 1) & (CI_BUFFER_SIZE - 1);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (address > CI_BUFFER_SIZE)
528c2ecf20Sopenharmony_ci		return -1;
538c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_READ_CMD | (1 << 16) | address,
548c2ecf20Sopenharmony_ci		  CI_DO_READ_ATTRIBUTES(ci->nr));
558c2ecf20Sopenharmony_ci	wait_ci_ready(ci);
568c2ecf20Sopenharmony_ci	val = 0xff & ddbreadl(ci->port->dev, CI_BUFFER(ci->nr) + off);
578c2ecf20Sopenharmony_ci	return val;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
618c2ecf20Sopenharmony_ci			       int address, u8 value)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_WRITE_CMD | (value << 16) | address,
668c2ecf20Sopenharmony_ci		  CI_DO_ATTRIBUTE_RW(ci->nr));
678c2ecf20Sopenharmony_ci	wait_ci_ready(ci);
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int read_cam_control(struct dvb_ca_en50221 *ca,
728c2ecf20Sopenharmony_ci			    int slot, u8 address)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u32 count = 100;
758c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
768c2ecf20Sopenharmony_ci	u32 res;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_READ_CMD | address,
798c2ecf20Sopenharmony_ci		  CI_DO_IO_RW(ci->nr));
808c2ecf20Sopenharmony_ci	ndelay(500);
818c2ecf20Sopenharmony_ci	do {
828c2ecf20Sopenharmony_ci		res = ddbreadl(ci->port->dev, CI_READDATA(ci->nr));
838c2ecf20Sopenharmony_ci		if (res & CI_READY)
848c2ecf20Sopenharmony_ci			break;
858c2ecf20Sopenharmony_ci		usleep_range(1, 2);
868c2ecf20Sopenharmony_ci		if ((--count) == 0)
878c2ecf20Sopenharmony_ci			return -1;
888c2ecf20Sopenharmony_ci	} while (1);
898c2ecf20Sopenharmony_ci	return 0xff & res;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
938c2ecf20Sopenharmony_ci			     u8 address, u8 value)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_WRITE_CMD | (value << 16) | address,
988c2ecf20Sopenharmony_ci		  CI_DO_IO_RW(ci->nr));
998c2ecf20Sopenharmony_ci	wait_ci_ready(ci);
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int slot_reset(struct dvb_ca_en50221 *ca, int slot)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_POWER_ON,
1088c2ecf20Sopenharmony_ci		  CI_CONTROL(ci->nr));
1098c2ecf20Sopenharmony_ci	msleep(100);
1108c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_POWER_ON | CI_RESET_CAM,
1118c2ecf20Sopenharmony_ci		  CI_CONTROL(ci->nr));
1128c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_ENABLE | CI_POWER_ON | CI_RESET_CAM,
1138c2ecf20Sopenharmony_ci		  CI_CONTROL(ci->nr));
1148c2ecf20Sopenharmony_ci	usleep_range(20, 25);
1158c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, CI_ENABLE | CI_POWER_ON,
1168c2ecf20Sopenharmony_ci		  CI_CONTROL(ci->nr));
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, 0, CI_CONTROL(ci->nr));
1258c2ecf20Sopenharmony_ci	msleep(300);
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
1328c2ecf20Sopenharmony_ci	u32 val = ddbreadl(ci->port->dev, CI_CONTROL(ci->nr));
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ddbwritel(ci->port->dev, val | CI_BYPASS_DISABLE,
1358c2ecf20Sopenharmony_ci		  CI_CONTROL(ci->nr));
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
1428c2ecf20Sopenharmony_ci	u32 val = ddbreadl(ci->port->dev, CI_CONTROL(ci->nr));
1438c2ecf20Sopenharmony_ci	int stat = 0;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (val & CI_CAM_DETECT)
1468c2ecf20Sopenharmony_ci		stat |= DVB_CA_EN50221_POLL_CAM_PRESENT;
1478c2ecf20Sopenharmony_ci	if (val & CI_CAM_READY)
1488c2ecf20Sopenharmony_ci		stat |= DVB_CA_EN50221_POLL_CAM_READY;
1498c2ecf20Sopenharmony_ci	return stat;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic struct dvb_ca_en50221 en_templ = {
1538c2ecf20Sopenharmony_ci	.read_attribute_mem  = read_attribute_mem,
1548c2ecf20Sopenharmony_ci	.write_attribute_mem = write_attribute_mem,
1558c2ecf20Sopenharmony_ci	.read_cam_control    = read_cam_control,
1568c2ecf20Sopenharmony_ci	.write_cam_control   = write_cam_control,
1578c2ecf20Sopenharmony_ci	.slot_reset          = slot_reset,
1588c2ecf20Sopenharmony_ci	.slot_shutdown       = slot_shutdown,
1598c2ecf20Sopenharmony_ci	.slot_ts_enable      = slot_ts_enable,
1608c2ecf20Sopenharmony_ci	.poll_slot_status    = poll_slot_status,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic void ci_attach(struct ddb_port *port)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct ddb_ci *ci;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ci = kzalloc(sizeof(*ci), GFP_KERNEL);
1688c2ecf20Sopenharmony_ci	if (!ci)
1698c2ecf20Sopenharmony_ci		return;
1708c2ecf20Sopenharmony_ci	memcpy(&ci->en, &en_templ, sizeof(en_templ));
1718c2ecf20Sopenharmony_ci	ci->en.data = ci;
1728c2ecf20Sopenharmony_ci	port->en = &ci->en;
1738c2ecf20Sopenharmony_ci	port->en_freedata = 1;
1748c2ecf20Sopenharmony_ci	ci->port = port;
1758c2ecf20Sopenharmony_ci	ci->nr = port->nr - 2;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* DuoFlex Dual CI support */
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int write_creg(struct ddb_ci *ci, u8 data, u8 mask)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &ci->port->i2c->adap;
1838c2ecf20Sopenharmony_ci	u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ci->port->creg = (ci->port->creg & ~mask) | data;
1868c2ecf20Sopenharmony_ci	return i2c_write_reg(i2c, adr, 0x02, ci->port->creg);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int read_attribute_mem_xo2(struct dvb_ca_en50221 *ca,
1908c2ecf20Sopenharmony_ci				  int slot, int address)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
1938c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &ci->port->i2c->adap;
1948c2ecf20Sopenharmony_ci	u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13;
1958c2ecf20Sopenharmony_ci	int res;
1968c2ecf20Sopenharmony_ci	u8 val;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	res = i2c_read_reg16(i2c, adr, 0x8000 | address, &val);
1998c2ecf20Sopenharmony_ci	return res ? res : val;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int write_attribute_mem_xo2(struct dvb_ca_en50221 *ca, int slot,
2038c2ecf20Sopenharmony_ci				   int address, u8 value)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2068c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &ci->port->i2c->adap;
2078c2ecf20Sopenharmony_ci	u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return i2c_write_reg16(i2c, adr, 0x8000 | address, value);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int read_cam_control_xo2(struct dvb_ca_en50221 *ca,
2138c2ecf20Sopenharmony_ci				int slot, u8 address)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2168c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &ci->port->i2c->adap;
2178c2ecf20Sopenharmony_ci	u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13;
2188c2ecf20Sopenharmony_ci	u8 val;
2198c2ecf20Sopenharmony_ci	int res;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	res = i2c_read_reg(i2c, adr, 0x20 | (address & 3), &val);
2228c2ecf20Sopenharmony_ci	return res ? res : val;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int write_cam_control_xo2(struct dvb_ca_en50221 *ca, int slot,
2268c2ecf20Sopenharmony_ci				 u8 address, u8 value)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2298c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &ci->port->i2c->adap;
2308c2ecf20Sopenharmony_ci	u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return i2c_write_reg(i2c, adr, 0x20 | (address & 3), value);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int slot_reset_xo2(struct dvb_ca_en50221 *ca, int slot)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	dev_dbg(ci->port->dev->dev, "%s\n", __func__);
2408c2ecf20Sopenharmony_ci	write_creg(ci, 0x01, 0x01);
2418c2ecf20Sopenharmony_ci	write_creg(ci, 0x04, 0x04);
2428c2ecf20Sopenharmony_ci	msleep(20);
2438c2ecf20Sopenharmony_ci	write_creg(ci, 0x02, 0x02);
2448c2ecf20Sopenharmony_ci	write_creg(ci, 0x00, 0x04);
2458c2ecf20Sopenharmony_ci	write_creg(ci, 0x18, 0x18);
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int slot_shutdown_xo2(struct dvb_ca_en50221 *ca, int slot)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	dev_dbg(ci->port->dev->dev, "%s\n", __func__);
2548c2ecf20Sopenharmony_ci	write_creg(ci, 0x10, 0xff);
2558c2ecf20Sopenharmony_ci	write_creg(ci, 0x08, 0x08);
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int slot_ts_enable_xo2(struct dvb_ca_en50221 *ca, int slot)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	dev_dbg(ci->port->dev->dev, "%s\n", __func__);
2648c2ecf20Sopenharmony_ci	write_creg(ci, 0x00, 0x10);
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int poll_slot_status_xo2(struct dvb_ca_en50221 *ca, int slot, int open)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct ddb_ci *ci = ca->data;
2718c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &ci->port->i2c->adap;
2728c2ecf20Sopenharmony_ci	u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13;
2738c2ecf20Sopenharmony_ci	u8 val = 0;
2748c2ecf20Sopenharmony_ci	int stat = 0;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	i2c_read_reg(i2c, adr, 0x01, &val);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (val & 2)
2798c2ecf20Sopenharmony_ci		stat |= DVB_CA_EN50221_POLL_CAM_PRESENT;
2808c2ecf20Sopenharmony_ci	if (val & 1)
2818c2ecf20Sopenharmony_ci		stat |= DVB_CA_EN50221_POLL_CAM_READY;
2828c2ecf20Sopenharmony_ci	return stat;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic struct dvb_ca_en50221 en_xo2_templ = {
2868c2ecf20Sopenharmony_ci	.read_attribute_mem  = read_attribute_mem_xo2,
2878c2ecf20Sopenharmony_ci	.write_attribute_mem = write_attribute_mem_xo2,
2888c2ecf20Sopenharmony_ci	.read_cam_control    = read_cam_control_xo2,
2898c2ecf20Sopenharmony_ci	.write_cam_control   = write_cam_control_xo2,
2908c2ecf20Sopenharmony_ci	.slot_reset          = slot_reset_xo2,
2918c2ecf20Sopenharmony_ci	.slot_shutdown       = slot_shutdown_xo2,
2928c2ecf20Sopenharmony_ci	.slot_ts_enable      = slot_ts_enable_xo2,
2938c2ecf20Sopenharmony_ci	.poll_slot_status    = poll_slot_status_xo2,
2948c2ecf20Sopenharmony_ci};
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void ci_xo2_attach(struct ddb_port *port)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct ddb_ci *ci;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ci = kzalloc(sizeof(*ci), GFP_KERNEL);
3018c2ecf20Sopenharmony_ci	if (!ci)
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci	memcpy(&ci->en, &en_xo2_templ, sizeof(en_xo2_templ));
3048c2ecf20Sopenharmony_ci	ci->en.data = ci;
3058c2ecf20Sopenharmony_ci	port->en = &ci->en;
3068c2ecf20Sopenharmony_ci	port->en_freedata = 1;
3078c2ecf20Sopenharmony_ci	ci->port = port;
3088c2ecf20Sopenharmony_ci	ci->nr = port->nr - 2;
3098c2ecf20Sopenharmony_ci	ci->port->creg = 0;
3108c2ecf20Sopenharmony_ci	write_creg(ci, 0x10, 0xff);
3118c2ecf20Sopenharmony_ci	write_creg(ci, 0x08, 0x08);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic const struct cxd2099_cfg cxd_cfgtmpl = {
3158c2ecf20Sopenharmony_ci	.bitrate =  72000,
3168c2ecf20Sopenharmony_ci	.polarity = 1,
3178c2ecf20Sopenharmony_ci	.clock_mode = 1,
3188c2ecf20Sopenharmony_ci	.max_i2c = 512,
3198c2ecf20Sopenharmony_ci};
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int ci_cxd2099_attach(struct ddb_port *port, u32 bitrate)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl;
3248c2ecf20Sopenharmony_ci	struct i2c_client *client;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	cxd_cfg.bitrate = bitrate;
3278c2ecf20Sopenharmony_ci	cxd_cfg.en = &port->en;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	client = dvb_module_probe("cxd2099", NULL, &port->i2c->adap,
3308c2ecf20Sopenharmony_ci				  0x40, &cxd_cfg);
3318c2ecf20Sopenharmony_ci	if (!client)
3328c2ecf20Sopenharmony_ci		goto err;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	port->dvb[0].i2c_client[0] = client;
3358c2ecf20Sopenharmony_ci	port->en_freedata = 0;
3368c2ecf20Sopenharmony_ci	return 0;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cierr:
3398c2ecf20Sopenharmony_ci	dev_err(port->dev->dev, "CXD2099AR attach failed\n");
3408c2ecf20Sopenharmony_ci	return -ENODEV;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ciint ddb_ci_attach(struct ddb_port *port, u32 bitrate)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	int ret;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	switch (port->type) {
3488c2ecf20Sopenharmony_ci	case DDB_CI_EXTERNAL_SONY:
3498c2ecf20Sopenharmony_ci		ret = ci_cxd2099_attach(port, bitrate);
3508c2ecf20Sopenharmony_ci		if (ret)
3518c2ecf20Sopenharmony_ci			return -ENODEV;
3528c2ecf20Sopenharmony_ci		break;
3538c2ecf20Sopenharmony_ci	case DDB_CI_EXTERNAL_XO2:
3548c2ecf20Sopenharmony_ci	case DDB_CI_EXTERNAL_XO2_B:
3558c2ecf20Sopenharmony_ci		ci_xo2_attach(port);
3568c2ecf20Sopenharmony_ci		break;
3578c2ecf20Sopenharmony_ci	case DDB_CI_INTERNAL:
3588c2ecf20Sopenharmony_ci		ci_attach(port);
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	default:
3618c2ecf20Sopenharmony_ci		return -ENODEV;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (!port->en)
3658c2ecf20Sopenharmony_ci		return -ENODEV;
3668c2ecf20Sopenharmony_ci	dvb_ca_en50221_init(port->dvb[0].adap, port->en, 0, 1);
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_civoid ddb_ci_detach(struct ddb_port *port)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	if (port->dvb[0].dev)
3738c2ecf20Sopenharmony_ci		dvb_unregister_device(port->dvb[0].dev);
3748c2ecf20Sopenharmony_ci	if (port->en) {
3758c2ecf20Sopenharmony_ci		dvb_ca_en50221_release(port->en);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		dvb_module_release(port->dvb[0].i2c_client[0]);
3788c2ecf20Sopenharmony_ci		port->dvb[0].i2c_client[0] = NULL;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		/* free alloc'ed memory if needed */
3818c2ecf20Sopenharmony_ci		if (port->en_freedata)
3828c2ecf20Sopenharmony_ci			kfree(port->en->data);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		port->en = NULL;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci}
387