291 lines
7.0 KiB
C
291 lines
7.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
Mantis PCI bridge driver
|
|
Copyright (C) Manu Abraham (abraham.manu@gmail.com)
|
|
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/signal.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#include <media/dmxdev.h>
|
|
#include <media/dvbdev.h>
|
|
#include <media/dvb_demux.h>
|
|
#include <media/dvb_frontend.h>
|
|
#include <media/dvb_net.h>
|
|
|
|
#include "mantis_common.h"
|
|
#include "mantis_dma.h"
|
|
#include "mantis_ca.h"
|
|
#include "mantis_ioc.h"
|
|
#include "mantis_dvb.h"
|
|
|
|
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
|
|
int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power)
|
|
{
|
|
struct mantis_hwconfig *config = mantis->hwconfig;
|
|
|
|
switch (power) {
|
|
case POWER_ON:
|
|
dprintk(MANTIS_DEBUG, 1, "Power ON");
|
|
mantis_gpio_set_bits(mantis, config->power, POWER_ON);
|
|
msleep(100);
|
|
mantis_gpio_set_bits(mantis, config->power, POWER_ON);
|
|
msleep(100);
|
|
break;
|
|
|
|
case POWER_OFF:
|
|
dprintk(MANTIS_DEBUG, 1, "Power OFF");
|
|
mantis_gpio_set_bits(mantis, config->power, POWER_OFF);
|
|
msleep(100);
|
|
break;
|
|
|
|
default:
|
|
dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mantis_frontend_power);
|
|
|
|
void mantis_frontend_soft_reset(struct mantis_pci *mantis)
|
|
{
|
|
struct mantis_hwconfig *config = mantis->hwconfig;
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "Frontend RESET");
|
|
mantis_gpio_set_bits(mantis, config->reset, 0);
|
|
msleep(100);
|
|
mantis_gpio_set_bits(mantis, config->reset, 0);
|
|
msleep(100);
|
|
mantis_gpio_set_bits(mantis, config->reset, 1);
|
|
msleep(100);
|
|
mantis_gpio_set_bits(mantis, config->reset, 1);
|
|
msleep(100);
|
|
|
|
return;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset);
|
|
|
|
static int mantis_frontend_shutdown(struct mantis_pci *mantis)
|
|
{
|
|
int err;
|
|
|
|
mantis_frontend_soft_reset(mantis);
|
|
err = mantis_frontend_power(mantis, POWER_OFF);
|
|
if (err != 0) {
|
|
dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
|
|
{
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
struct mantis_pci *mantis = dvbdmx->priv;
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed");
|
|
if (!dvbdmx->dmx.frontend) {
|
|
dprintk(MANTIS_DEBUG, 1, "no frontend ?");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mantis->feeds++;
|
|
dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds);
|
|
|
|
if (mantis->feeds == 1) {
|
|
dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma");
|
|
mantis_dma_start(mantis);
|
|
tasklet_enable(&mantis->tasklet);
|
|
}
|
|
|
|
return mantis->feeds;
|
|
}
|
|
|
|
static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
|
|
{
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
struct mantis_pci *mantis = dvbdmx->priv;
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed");
|
|
if (!dvbdmx->dmx.frontend) {
|
|
dprintk(MANTIS_DEBUG, 1, "no frontend ?");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mantis->feeds--;
|
|
if (mantis->feeds == 0) {
|
|
dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma");
|
|
tasklet_disable(&mantis->tasklet);
|
|
mantis_dma_stop(mantis);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mantis_dvb_init(struct mantis_pci *mantis)
|
|
{
|
|
struct mantis_hwconfig *config = mantis->hwconfig;
|
|
int result;
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter");
|
|
|
|
result = dvb_register_adapter(&mantis->dvb_adapter,
|
|
"Mantis DVB adapter",
|
|
THIS_MODULE,
|
|
&mantis->pdev->dev,
|
|
adapter_nr);
|
|
|
|
if (result < 0) {
|
|
|
|
dprintk(MANTIS_ERROR, 1, "Error registering adapter");
|
|
return -ENODEV;
|
|
}
|
|
|
|
mantis->dvb_adapter.priv = mantis;
|
|
mantis->demux.dmx.capabilities = DMX_TS_FILTERING |
|
|
DMX_SECTION_FILTERING |
|
|
DMX_MEMORY_BASED_FILTERING;
|
|
|
|
mantis->demux.priv = mantis;
|
|
mantis->demux.filternum = 256;
|
|
mantis->demux.feednum = 256;
|
|
mantis->demux.start_feed = mantis_dvb_start_feed;
|
|
mantis->demux.stop_feed = mantis_dvb_stop_feed;
|
|
mantis->demux.write_to_decoder = NULL;
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init");
|
|
result = dvb_dmx_init(&mantis->demux);
|
|
if (result < 0) {
|
|
dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
|
|
|
|
goto err0;
|
|
}
|
|
|
|
mantis->dmxdev.filternum = 256;
|
|
mantis->dmxdev.demux = &mantis->demux.dmx;
|
|
mantis->dmxdev.capabilities = 0;
|
|
dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init");
|
|
|
|
result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter);
|
|
if (result < 0) {
|
|
|
|
dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result);
|
|
goto err1;
|
|
}
|
|
|
|
mantis->fe_hw.source = DMX_FRONTEND_0;
|
|
result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw);
|
|
if (result < 0) {
|
|
|
|
dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
|
|
goto err2;
|
|
}
|
|
|
|
mantis->fe_mem.source = DMX_MEMORY_FE;
|
|
result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem);
|
|
if (result < 0) {
|
|
dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
|
|
goto err3;
|
|
}
|
|
|
|
result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw);
|
|
if (result < 0) {
|
|
dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
|
|
goto err4;
|
|
}
|
|
|
|
dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx);
|
|
tasklet_setup(&mantis->tasklet, mantis_dma_xfer);
|
|
tasklet_disable(&mantis->tasklet);
|
|
if (mantis->hwconfig) {
|
|
result = config->frontend_init(mantis, mantis->fe);
|
|
if (result < 0) {
|
|
dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!");
|
|
goto err5;
|
|
} else {
|
|
if (mantis->fe == NULL) {
|
|
result = -ENOMEM;
|
|
dprintk(MANTIS_ERROR, 1, "FE <NULL>");
|
|
goto err5;
|
|
}
|
|
result = dvb_register_frontend(&mantis->dvb_adapter, mantis->fe);
|
|
if (result) {
|
|
dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed");
|
|
|
|
if (mantis->fe->ops.release)
|
|
mantis->fe->ops.release(mantis->fe);
|
|
|
|
mantis->fe = NULL;
|
|
goto err5;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
/* Error conditions .. */
|
|
err5:
|
|
tasklet_kill(&mantis->tasklet);
|
|
dvb_net_release(&mantis->dvbnet);
|
|
if (mantis->fe) {
|
|
dvb_unregister_frontend(mantis->fe);
|
|
dvb_frontend_detach(mantis->fe);
|
|
}
|
|
err4:
|
|
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
|
|
|
|
err3:
|
|
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
|
|
|
|
err2:
|
|
dvb_dmxdev_release(&mantis->dmxdev);
|
|
|
|
err1:
|
|
dvb_dmx_release(&mantis->demux);
|
|
|
|
err0:
|
|
dvb_unregister_adapter(&mantis->dvb_adapter);
|
|
|
|
return result;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mantis_dvb_init);
|
|
|
|
int mantis_dvb_exit(struct mantis_pci *mantis)
|
|
{
|
|
int err;
|
|
|
|
if (mantis->fe) {
|
|
/* mantis_ca_exit(mantis); */
|
|
err = mantis_frontend_shutdown(mantis);
|
|
if (err != 0)
|
|
dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err);
|
|
dvb_unregister_frontend(mantis->fe);
|
|
dvb_frontend_detach(mantis->fe);
|
|
}
|
|
|
|
tasklet_kill(&mantis->tasklet);
|
|
dvb_net_release(&mantis->dvbnet);
|
|
|
|
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
|
|
mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
|
|
|
|
dvb_dmxdev_release(&mantis->dmxdev);
|
|
dvb_dmx_release(&mantis->demux);
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter");
|
|
dvb_unregister_adapter(&mantis->dvb_adapter);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mantis_dvb_exit);
|