c8sectpfe-common.c (5926B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * c8sectpfe-common.c - C8SECTPFE STi DVB driver 4 * 5 * Copyright (c) STMicroelectronics 2015 6 * 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}