linux/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
<<
>>
Prefs
   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}
 263