263 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * c8sectpfe-common.c - C8SECTPFE STi DVB driver4 *5 * Copyright (c) STMicroelectronics 20156 *7 * Author: Peter Griffin <peter.griffin@linaro.org>8 *9 */10#include <linux/completion.h>11#include <linux/delay.h>12#include <linux/device.h>13#include <linux/dvb/dmx.h>14#include <linux/errno.h>15#include <linux/init.h>16#include <linux/interrupt.h>17#include <linux/io.h>18#include <linux/ioport.h>19#include <linux/module.h>20#include <linux/slab.h>21#include <linux/time.h>22#include <linux/wait.h>23 24#include <media/dmxdev.h>25#include <media/dvbdev.h>26#include <media/dvb_demux.h>27#include <media/dvb_frontend.h>28#include <media/dvb_net.h>29 30#include "c8sectpfe-common.h"31#include "c8sectpfe-core.h"32#include "c8sectpfe-dvb.h"33 34static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap,35 void *start_feed, void *stop_feed,36 struct c8sectpfei *fei)37{38 int result;39 40 demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING |41 DMX_SECTION_FILTERING |42 DMX_MEMORY_BASED_FILTERING;43 44 demux->dvb_demux.priv = demux;45 demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL;46 demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL;47 48 demux->dvb_demux.start_feed = start_feed;49 demux->dvb_demux.stop_feed = stop_feed;50 demux->dvb_demux.write_to_decoder = NULL;51 52 result = dvb_dmx_init(&demux->dvb_demux);53 if (result < 0) {54 dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n",55 result);56 goto err_dmx;57 }58 59 demux->dmxdev.filternum = demux->dvb_demux.filternum;60 demux->dmxdev.demux = &demux->dvb_demux.dmx;61 demux->dmxdev.capabilities = 0;62 63 result = dvb_dmxdev_init(&demux->dmxdev, adap);64 if (result < 0) {65 dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n",66 result);67 68 goto err_dmxdev;69 }70 71 demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index;72 73 result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,74 &demux->hw_frontend);75 if (result < 0) {76 dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result);77 goto err_fe_hw;78 }79 80 demux->mem_frontend.source = DMX_MEMORY_FE;81 result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,82 &demux->mem_frontend);83 if (result < 0) {84 dev_err(fei->dev, "add_frontend failed (%d)\n", result);85 goto err_fe_mem;86 }87 88 result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx,89 &demux->hw_frontend);90 if (result < 0) {91 dev_err(fei->dev, "connect_frontend (%d)\n", result);92 goto err_fe_con;93 }94 95 return 0;96 97err_fe_con:98 demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,99 &demux->mem_frontend);100err_fe_mem:101 demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,102 &demux->hw_frontend);103err_fe_hw:104 dvb_dmxdev_release(&demux->dmxdev);105err_dmxdev:106 dvb_dmx_release(&demux->dvb_demux);107err_dmx:108 return result;109 110}111 112static void unregister_dvb(struct stdemux *demux)113{114 115 demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,116 &demux->mem_frontend);117 118 demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,119 &demux->hw_frontend);120 121 dvb_dmxdev_release(&demux->dmxdev);122 123 dvb_dmx_release(&demux->dvb_demux);124}125 126static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei,127 void *start_feed,128 void *stop_feed)129{130 struct c8sectpfe *c8sectpfe;131 int result;132 int i, j;133 134 short int ids[] = { -1 };135 136 c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL);137 if (!c8sectpfe)138 goto err1;139 140 mutex_init(&c8sectpfe->lock);141 142 c8sectpfe->device = fei->dev;143 144 result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe",145 THIS_MODULE, fei->dev, ids);146 if (result < 0) {147 dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n",148 result);149 goto err2;150 }151 152 c8sectpfe->adapter.priv = fei;153 154 for (i = 0; i < fei->tsin_count; i++) {155 156 c8sectpfe->demux[i].tsin_index = i;157 c8sectpfe->demux[i].c8sectpfei = fei;158 159 result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter,160 start_feed, stop_feed, fei);161 if (result < 0) {162 dev_err(fei->dev,163 "register_dvb feed=%d failed (errno = %d)\n",164 result, i);165 166 /* we take a all or nothing approach */167 for (j = 0; j < i; j++)168 unregister_dvb(&c8sectpfe->demux[j]);169 goto err3;170 }171 }172 173 c8sectpfe->num_feeds = fei->tsin_count;174 175 return c8sectpfe;176err3:177 dvb_unregister_adapter(&c8sectpfe->adapter);178err2:179 kfree(c8sectpfe);180err1:181 return NULL;182};183 184static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe)185{186 int i;187 188 if (!c8sectpfe)189 return;190 191 for (i = 0; i < c8sectpfe->num_feeds; i++)192 unregister_dvb(&c8sectpfe->demux[i]);193 194 dvb_unregister_adapter(&c8sectpfe->adapter);195 196 kfree(c8sectpfe);197};198 199void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,200 struct c8sectpfei *fei)201{202 int n;203 struct channel_info *tsin;204 205 for (n = 0; n < fei->tsin_count; n++) {206 207 tsin = fei->channel_data[n];208 209 if (tsin) {210 if (tsin->frontend) {211 dvb_unregister_frontend(tsin->frontend);212 dvb_frontend_detach(tsin->frontend);213 }214 215 i2c_put_adapter(tsin->i2c_adapter);216 217 if (tsin->i2c_client) {218 module_put(tsin->i2c_client->dev.driver->owner);219 i2c_unregister_device(tsin->i2c_client);220 }221 }222 }223 224 c8sectpfe_delete(c8sectpfe);225};226 227int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,228 struct c8sectpfei *fei,229 void *start_feed,230 void *stop_feed)231{232 struct channel_info *tsin;233 struct dvb_frontend *frontend;234 int n, res;235 236 *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed);237 if (!*c8sectpfe)238 return -ENOMEM;239 240 for (n = 0; n < fei->tsin_count; n++) {241 tsin = fei->channel_data[n];242 243 res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n);244 if (res)245 goto err;246 247 res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend);248 if (res < 0) {249 dev_err(fei->dev, "dvb_register_frontend failed (%d)\n",250 res);251 goto err;252 }253 254 tsin->frontend = frontend;255 }256 257 return 0;258 259err:260 c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei);261 return res;262}263