cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cxd2880-spi.c (15062B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * cxd2880-spi.c
      4 * Sony CXD2880 DVB-T2/T tuner + demodulator driver
      5 * SPI adapter
      6 *
      7 * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
      8 */
      9
     10#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
     11
     12#include <linux/spi/spi.h>
     13#include <linux/regulator/consumer.h>
     14#include <linux/ktime.h>
     15
     16#include <media/dvb_demux.h>
     17#include <media/dmxdev.h>
     18#include <media/dvb_frontend.h>
     19#include "cxd2880.h"
     20
     21#define CXD2880_MAX_FILTER_SIZE 32
     22#define BURST_WRITE_MAX 128
     23#define MAX_TRANS_PKT 300
     24
     25struct cxd2880_ts_buf_info {
     26	u8 read_ready:1;
     27	u8 almost_full:1;
     28	u8 almost_empty:1;
     29	u8 overflow:1;
     30	u8 underflow:1;
     31	u16 pkt_num;
     32};
     33
     34struct cxd2880_pid_config {
     35	u8 is_enable;
     36	u16 pid;
     37};
     38
     39struct cxd2880_pid_filter_config {
     40	u8 is_negative;
     41	struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
     42};
     43
     44struct cxd2880_dvb_spi {
     45	struct dvb_frontend dvb_fe;
     46	struct dvb_adapter adapter;
     47	struct dvb_demux demux;
     48	struct dmxdev dmxdev;
     49	struct dmx_frontend dmx_fe;
     50	struct task_struct *cxd2880_ts_read_thread;
     51	struct spi_device *spi;
     52	struct mutex spi_mutex; /* For SPI access exclusive control */
     53	int feed_count;
     54	int all_pid_feed_count;
     55	struct regulator *vcc_supply;
     56	u8 *ts_buf;
     57	struct cxd2880_pid_filter_config filter_config;
     58};
     59
     60DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
     61
     62static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
     63{
     64	struct spi_message msg;
     65	struct spi_transfer tx = {};
     66
     67	if (!spi || !data) {
     68		pr_err("invalid arg\n");
     69		return -EINVAL;
     70	}
     71
     72	tx.tx_buf = data;
     73	tx.len = size;
     74
     75	spi_message_init(&msg);
     76	spi_message_add_tail(&tx, &msg);
     77
     78	return spi_sync(spi, &msg);
     79}
     80
     81static int cxd2880_write_reg(struct spi_device *spi,
     82			     u8 sub_address, const u8 *data, u32 size)
     83{
     84	u8 send_data[BURST_WRITE_MAX + 4];
     85	const u8 *write_data_top = NULL;
     86	int ret = 0;
     87
     88	if (!spi || !data) {
     89		pr_err("invalid arg\n");
     90		return -EINVAL;
     91	}
     92	if (size > BURST_WRITE_MAX || size > U8_MAX) {
     93		pr_err("data size > WRITE_MAX\n");
     94		return -EINVAL;
     95	}
     96
     97	if (sub_address + size > 0x100) {
     98		pr_err("out of range\n");
     99		return -EINVAL;
    100	}
    101
    102	send_data[0] = 0x0e;
    103	write_data_top = data;
    104
    105	send_data[1] = sub_address;
    106	send_data[2] = (u8)size;
    107
    108	memcpy(&send_data[3], write_data_top, send_data[2]);
    109
    110	ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
    111	if (ret)
    112		pr_err("write spi failed %d\n", ret);
    113
    114	return ret;
    115}
    116
    117static int cxd2880_spi_read_ts(struct spi_device *spi,
    118			       u8 *read_data,
    119			       u32 packet_num)
    120{
    121	int ret;
    122	u8 data[3];
    123	struct spi_message message;
    124	struct spi_transfer transfer[2] = {};
    125
    126	if (!spi || !read_data || !packet_num) {
    127		pr_err("invalid arg\n");
    128		return -EINVAL;
    129	}
    130	if (packet_num > 0xffff) {
    131		pr_err("packet num > 0xffff\n");
    132		return -EINVAL;
    133	}
    134
    135	data[0] = 0x10;
    136	data[1] = packet_num >> 8;
    137	data[2] = packet_num;
    138
    139	spi_message_init(&message);
    140
    141	transfer[0].len = 3;
    142	transfer[0].tx_buf = data;
    143	spi_message_add_tail(&transfer[0], &message);
    144	transfer[1].len = packet_num * 188;
    145	transfer[1].rx_buf = read_data;
    146	spi_message_add_tail(&transfer[1], &message);
    147
    148	ret = spi_sync(spi, &message);
    149	if (ret)
    150		pr_err("spi_sync failed\n");
    151
    152	return ret;
    153}
    154
    155static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
    156					   struct cxd2880_ts_buf_info *info)
    157{
    158	u8 send_data = 0x20;
    159	u8 recv_data[2];
    160	int ret;
    161
    162	if (!spi || !info) {
    163		pr_err("invalid arg\n");
    164		return -EINVAL;
    165	}
    166
    167	ret = spi_write_then_read(spi, &send_data, 1,
    168				  recv_data, sizeof(recv_data));
    169	if (ret)
    170		pr_err("spi_write_then_read failed\n");
    171
    172	info->read_ready = (recv_data[0] & 0x80) ? 1 : 0;
    173	info->almost_full = (recv_data[0] & 0x40) ? 1 : 0;
    174	info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0;
    175	info->overflow = (recv_data[0] & 0x10) ? 1 : 0;
    176	info->underflow = (recv_data[0] & 0x08) ? 1 : 0;
    177	info->pkt_num = ((recv_data[0] & 0x07) << 8) | recv_data[1];
    178
    179	return ret;
    180}
    181
    182static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
    183{
    184	u8 data = 0x03;
    185	int ret;
    186
    187	ret = cxd2880_write_spi(spi, &data, 1);
    188
    189	if (ret)
    190		pr_err("write spi failed\n");
    191
    192	return ret;
    193}
    194
    195static int cxd2880_set_pid_filter(struct spi_device *spi,
    196				  struct cxd2880_pid_filter_config *cfg)
    197{
    198	u8 data[65];
    199	int i;
    200	u16 pid = 0;
    201	int ret;
    202
    203	if (!spi) {
    204		pr_err("invalid arg\n");
    205		return -EINVAL;
    206	}
    207
    208	data[0] = 0x00;
    209	ret = cxd2880_write_reg(spi, 0x00, &data[0], 1);
    210	if (ret)
    211		return ret;
    212	if (!cfg) {
    213		data[0] = 0x02;
    214		ret = cxd2880_write_reg(spi, 0x50, &data[0], 1);
    215	} else {
    216		data[0] = cfg->is_negative ? 0x01 : 0x00;
    217
    218		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
    219			pid = cfg->pid_config[i].pid;
    220			if (cfg->pid_config[i].is_enable) {
    221				data[1 + (i * 2)] = (pid >> 8) | 0x20;
    222				data[2 + (i * 2)] = pid & 0xff;
    223			} else {
    224				data[1 + (i * 2)] = 0x00;
    225				data[2 + (i * 2)] = 0x00;
    226			}
    227		}
    228		ret = cxd2880_write_reg(spi, 0x50, data, 65);
    229	}
    230
    231	return ret;
    232}
    233
    234static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
    235				     struct cxd2880_pid_filter_config *cfg,
    236				     bool is_all_pid_filter)
    237{
    238	int ret;
    239
    240	if (!dvb_spi || !cfg) {
    241		pr_err("invalid arg.\n");
    242		return -EINVAL;
    243	}
    244
    245	mutex_lock(&dvb_spi->spi_mutex);
    246	if (is_all_pid_filter) {
    247		struct cxd2880_pid_filter_config tmpcfg;
    248
    249		memset(&tmpcfg, 0, sizeof(tmpcfg));
    250		tmpcfg.is_negative = 1;
    251		tmpcfg.pid_config[0].is_enable = 1;
    252		tmpcfg.pid_config[0].pid = 0x1fff;
    253
    254		ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
    255	} else {
    256		ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
    257	}
    258	mutex_unlock(&dvb_spi->spi_mutex);
    259
    260	if (ret)
    261		pr_err("set_pid_filter failed\n");
    262
    263	return ret;
    264}
    265
    266static int cxd2880_ts_read(void *arg)
    267{
    268	struct cxd2880_dvb_spi *dvb_spi = NULL;
    269	struct cxd2880_ts_buf_info info;
    270	ktime_t start;
    271	u32 i;
    272	int ret;
    273
    274	dvb_spi = arg;
    275	if (!dvb_spi) {
    276		pr_err("invalid arg\n");
    277		return -EINVAL;
    278	}
    279
    280	ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
    281	if (ret) {
    282		pr_err("set_clear_ts_buffer failed\n");
    283		return ret;
    284	}
    285
    286	start = ktime_get();
    287	while (!kthread_should_stop()) {
    288		ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
    289						      &info);
    290		if (ret) {
    291			pr_err("spi_read_ts_buffer_info error\n");
    292			return ret;
    293		}
    294
    295		if (info.pkt_num > MAX_TRANS_PKT) {
    296			for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++) {
    297				cxd2880_spi_read_ts(dvb_spi->spi,
    298						    dvb_spi->ts_buf,
    299						    MAX_TRANS_PKT);
    300				dvb_dmx_swfilter(&dvb_spi->demux,
    301						 dvb_spi->ts_buf,
    302						 MAX_TRANS_PKT * 188);
    303			}
    304			start = ktime_get();
    305		} else if ((info.pkt_num > 0) &&
    306			   (ktime_to_ms(ktime_sub(ktime_get(), start)) >= 500)) {
    307			cxd2880_spi_read_ts(dvb_spi->spi,
    308					    dvb_spi->ts_buf,
    309					    info.pkt_num);
    310			dvb_dmx_swfilter(&dvb_spi->demux,
    311					 dvb_spi->ts_buf,
    312					 info.pkt_num * 188);
    313			start = ktime_get();
    314		} else {
    315			usleep_range(10000, 11000);
    316		}
    317	}
    318
    319	return 0;
    320}
    321
    322static int cxd2880_start_feed(struct dvb_demux_feed *feed)
    323{
    324	int ret = 0;
    325	int i = 0;
    326	struct dvb_demux *demux = NULL;
    327	struct cxd2880_dvb_spi *dvb_spi = NULL;
    328
    329	if (!feed) {
    330		pr_err("invalid arg\n");
    331		return -EINVAL;
    332	}
    333
    334	demux = feed->demux;
    335	if (!demux) {
    336		pr_err("feed->demux is NULL\n");
    337		return -EINVAL;
    338	}
    339	dvb_spi = demux->priv;
    340
    341	if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
    342		pr_err("Exceeded maximum PID count (32).");
    343		pr_err("Selected PID cannot be enabled.\n");
    344		return -EINVAL;
    345	}
    346
    347	if (feed->pid == 0x2000) {
    348		if (dvb_spi->all_pid_feed_count == 0) {
    349			ret = cxd2880_update_pid_filter(dvb_spi,
    350							&dvb_spi->filter_config,
    351							true);
    352			if (ret) {
    353				pr_err("update pid filter failed\n");
    354				return ret;
    355			}
    356		}
    357		dvb_spi->all_pid_feed_count++;
    358
    359		pr_debug("all PID feed (count = %d)\n",
    360			 dvb_spi->all_pid_feed_count);
    361	} else {
    362		struct cxd2880_pid_filter_config cfgtmp;
    363
    364		cfgtmp = dvb_spi->filter_config;
    365
    366		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
    367			if (cfgtmp.pid_config[i].is_enable == 0) {
    368				cfgtmp.pid_config[i].is_enable = 1;
    369				cfgtmp.pid_config[i].pid = feed->pid;
    370				pr_debug("store PID %d to #%d\n",
    371					 feed->pid, i);
    372				break;
    373			}
    374		}
    375		if (i == CXD2880_MAX_FILTER_SIZE) {
    376			pr_err("PID filter is full.\n");
    377			return -EINVAL;
    378		}
    379		if (!dvb_spi->all_pid_feed_count)
    380			ret = cxd2880_update_pid_filter(dvb_spi,
    381							&cfgtmp,
    382							false);
    383		if (ret)
    384			return ret;
    385
    386		dvb_spi->filter_config = cfgtmp;
    387	}
    388
    389	if (dvb_spi->feed_count == 0) {
    390		dvb_spi->ts_buf =
    391			kmalloc(MAX_TRANS_PKT * 188,
    392				GFP_KERNEL | GFP_DMA);
    393		if (!dvb_spi->ts_buf) {
    394			pr_err("ts buffer allocate failed\n");
    395			memset(&dvb_spi->filter_config, 0,
    396			       sizeof(dvb_spi->filter_config));
    397			dvb_spi->all_pid_feed_count = 0;
    398			return -ENOMEM;
    399		}
    400		dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
    401							      dvb_spi,
    402							      "cxd2880_ts_read");
    403		if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
    404			pr_err("kthread_run failed\n");
    405			kfree(dvb_spi->ts_buf);
    406			dvb_spi->ts_buf = NULL;
    407			memset(&dvb_spi->filter_config, 0,
    408			       sizeof(dvb_spi->filter_config));
    409			dvb_spi->all_pid_feed_count = 0;
    410			return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
    411		}
    412	}
    413
    414	dvb_spi->feed_count++;
    415
    416	pr_debug("start feed (count %d)\n", dvb_spi->feed_count);
    417	return 0;
    418}
    419
    420static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
    421{
    422	int i = 0;
    423	int ret;
    424	struct dvb_demux *demux = NULL;
    425	struct cxd2880_dvb_spi *dvb_spi = NULL;
    426
    427	if (!feed) {
    428		pr_err("invalid arg\n");
    429		return -EINVAL;
    430	}
    431
    432	demux = feed->demux;
    433	if (!demux) {
    434		pr_err("feed->demux is NULL\n");
    435		return -EINVAL;
    436	}
    437	dvb_spi = demux->priv;
    438
    439	if (!dvb_spi->feed_count) {
    440		pr_err("no feed is started\n");
    441		return -EINVAL;
    442	}
    443
    444	if (feed->pid == 0x2000) {
    445		/*
    446		 * Special PID case.
    447		 * Number of 0x2000 feed request was stored
    448		 * in dvb_spi->all_pid_feed_count.
    449		 */
    450		if (dvb_spi->all_pid_feed_count <= 0) {
    451			pr_err("PID %d not found\n", feed->pid);
    452			return -EINVAL;
    453		}
    454		dvb_spi->all_pid_feed_count--;
    455	} else {
    456		struct cxd2880_pid_filter_config cfgtmp;
    457
    458		cfgtmp = dvb_spi->filter_config;
    459
    460		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
    461			if (feed->pid == cfgtmp.pid_config[i].pid &&
    462			    cfgtmp.pid_config[i].is_enable != 0) {
    463				cfgtmp.pid_config[i].is_enable = 0;
    464				cfgtmp.pid_config[i].pid = 0;
    465				pr_debug("removed PID %d from #%d\n",
    466					 feed->pid, i);
    467				break;
    468			}
    469		}
    470		dvb_spi->filter_config = cfgtmp;
    471
    472		if (i == CXD2880_MAX_FILTER_SIZE) {
    473			pr_err("PID %d not found\n", feed->pid);
    474			return -EINVAL;
    475		}
    476	}
    477
    478	ret = cxd2880_update_pid_filter(dvb_spi,
    479					&dvb_spi->filter_config,
    480					dvb_spi->all_pid_feed_count > 0);
    481	dvb_spi->feed_count--;
    482
    483	if (dvb_spi->feed_count == 0) {
    484		int ret_stop = 0;
    485
    486		ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
    487		if (ret_stop) {
    488			pr_err("kthread_stop failed. (%d)\n", ret_stop);
    489			ret = ret_stop;
    490		}
    491		kfree(dvb_spi->ts_buf);
    492		dvb_spi->ts_buf = NULL;
    493	}
    494
    495	pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count);
    496
    497	return ret;
    498}
    499
    500static const struct of_device_id cxd2880_spi_of_match[] = {
    501	{ .compatible = "sony,cxd2880" },
    502	{ /* sentinel */ }
    503};
    504
    505MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
    506
    507static int
    508cxd2880_spi_probe(struct spi_device *spi)
    509{
    510	int ret;
    511	struct cxd2880_dvb_spi *dvb_spi = NULL;
    512	struct cxd2880_config config;
    513
    514	if (!spi) {
    515		pr_err("invalid arg\n");
    516		return -EINVAL;
    517	}
    518
    519	dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
    520	if (!dvb_spi)
    521		return -ENOMEM;
    522
    523	dvb_spi->vcc_supply = devm_regulator_get_optional(&spi->dev, "vcc");
    524	if (IS_ERR(dvb_spi->vcc_supply)) {
    525		if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) {
    526			ret = -EPROBE_DEFER;
    527			goto fail_regulator;
    528		}
    529		dvb_spi->vcc_supply = NULL;
    530	} else {
    531		ret = regulator_enable(dvb_spi->vcc_supply);
    532		if (ret)
    533			goto fail_regulator;
    534	}
    535
    536	dvb_spi->spi = spi;
    537	mutex_init(&dvb_spi->spi_mutex);
    538	spi_set_drvdata(spi, dvb_spi);
    539	config.spi = spi;
    540	config.spi_mutex = &dvb_spi->spi_mutex;
    541
    542	ret = dvb_register_adapter(&dvb_spi->adapter,
    543				   "CXD2880",
    544				   THIS_MODULE,
    545				   &spi->dev,
    546				   adapter_nr);
    547	if (ret < 0) {
    548		pr_err("dvb_register_adapter() failed\n");
    549		goto fail_adapter;
    550	}
    551
    552	if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
    553		pr_err("cxd2880_attach failed\n");
    554		ret = -ENODEV;
    555		goto fail_attach;
    556	}
    557
    558	ret = dvb_register_frontend(&dvb_spi->adapter,
    559				    &dvb_spi->dvb_fe);
    560	if (ret < 0) {
    561		pr_err("dvb_register_frontend() failed\n");
    562		goto fail_frontend;
    563	}
    564
    565	dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
    566	dvb_spi->demux.priv = dvb_spi;
    567	dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
    568	dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
    569	dvb_spi->demux.start_feed = cxd2880_start_feed;
    570	dvb_spi->demux.stop_feed = cxd2880_stop_feed;
    571
    572	ret = dvb_dmx_init(&dvb_spi->demux);
    573	if (ret < 0) {
    574		pr_err("dvb_dmx_init() failed\n");
    575		goto fail_dmx;
    576	}
    577
    578	dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
    579	dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
    580	dvb_spi->dmxdev.capabilities = 0;
    581	ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
    582			      &dvb_spi->adapter);
    583	if (ret < 0) {
    584		pr_err("dvb_dmxdev_init() failed\n");
    585		goto fail_dmxdev;
    586	}
    587
    588	dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
    589	ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
    590					      &dvb_spi->dmx_fe);
    591	if (ret < 0) {
    592		pr_err("add_frontend() failed\n");
    593		goto fail_dmx_fe;
    594	}
    595
    596	ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
    597						  &dvb_spi->dmx_fe);
    598	if (ret < 0) {
    599		pr_err("connect_frontend() failed\n");
    600		goto fail_fe_conn;
    601	}
    602
    603	pr_info("Sony CXD2880 has successfully attached.\n");
    604
    605	return 0;
    606
    607fail_fe_conn:
    608	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
    609					   &dvb_spi->dmx_fe);
    610fail_dmx_fe:
    611	dvb_dmxdev_release(&dvb_spi->dmxdev);
    612fail_dmxdev:
    613	dvb_dmx_release(&dvb_spi->demux);
    614fail_dmx:
    615	dvb_unregister_frontend(&dvb_spi->dvb_fe);
    616fail_frontend:
    617	dvb_frontend_detach(&dvb_spi->dvb_fe);
    618fail_attach:
    619	dvb_unregister_adapter(&dvb_spi->adapter);
    620fail_adapter:
    621	if (dvb_spi->vcc_supply)
    622		regulator_disable(dvb_spi->vcc_supply);
    623fail_regulator:
    624	kfree(dvb_spi);
    625	return ret;
    626}
    627
    628static void
    629cxd2880_spi_remove(struct spi_device *spi)
    630{
    631	struct cxd2880_dvb_spi *dvb_spi = spi_get_drvdata(spi);
    632
    633	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
    634					   &dvb_spi->dmx_fe);
    635	dvb_dmxdev_release(&dvb_spi->dmxdev);
    636	dvb_dmx_release(&dvb_spi->demux);
    637	dvb_unregister_frontend(&dvb_spi->dvb_fe);
    638	dvb_frontend_detach(&dvb_spi->dvb_fe);
    639	dvb_unregister_adapter(&dvb_spi->adapter);
    640
    641	if (dvb_spi->vcc_supply)
    642		regulator_disable(dvb_spi->vcc_supply);
    643
    644	kfree(dvb_spi);
    645	pr_info("cxd2880_spi remove ok.\n");
    646}
    647
    648static const struct spi_device_id cxd2880_spi_id[] = {
    649	{ "cxd2880", 0 },
    650	{ /* sentinel */ }
    651};
    652MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
    653
    654static struct spi_driver cxd2880_spi_driver = {
    655	.driver	= {
    656		.name	= "cxd2880",
    657		.of_match_table = cxd2880_spi_of_match,
    658	},
    659	.id_table = cxd2880_spi_id,
    660	.probe    = cxd2880_spi_probe,
    661	.remove   = cxd2880_spi_remove,
    662};
    663module_spi_driver(cxd2880_spi_driver);
    664
    665MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver SPI adapter");
    666MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
    667MODULE_LICENSE("GPL v2");