forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
7.0 KiB
290 lines
7.0 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
Mantis PCI bridge driver |
|
Copyright (C) Manu Abraham ([email protected]) |
|
|
|
*/ |
|
|
|
#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);
|
|
|