| // 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); | |
| enable_and_queue_work(system_bh_wq, &mantis->bh_work); | |
| } | |
| 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"); | |
| disable_work_sync(&mantis->bh_work); | |
| 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); | |
| INIT_WORK(&mantis->bh_work, mantis_dma_xfer); | |
| disable_work_sync(&mantis->bh_work); | |
| 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: | |
| cancel_work_sync(&mantis->bh_work); | |
| 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); | |
| } | |
| cancel_work_sync(&mantis->bh_work); | |
| 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); |