162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * some helper function for simple DVB cards which simply DMA the
562306a36Sopenharmony_ci * complete transport stream and let the computer sort everything else
662306a36Sopenharmony_ci * (i.e. we are using the software demux, ...).  Also uses vb2
762306a36Sopenharmony_ci * to manage DMA buffers.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <media/videobuf2-dvb.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciMODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
2262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int dvb_fnc(struct vb2_buffer *vb, void *priv)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct vb2_dvb *dvb = priv;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	dvb_dmx_swfilter(&dvb->demux, vb2_plane_vaddr(vb, 0),
3162306a36Sopenharmony_ci				      vb2_get_plane_payload(vb, 0));
3262306a36Sopenharmony_ci	return 0;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int vb2_dvb_start_feed(struct dvb_demux_feed *feed)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
3862306a36Sopenharmony_ci	struct vb2_dvb *dvb = demux->priv;
3962306a36Sopenharmony_ci	int rc = 0;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!demux->dmx.frontend)
4262306a36Sopenharmony_ci		return -EINVAL;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	mutex_lock(&dvb->lock);
4562306a36Sopenharmony_ci	dvb->nfeeds++;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (!dvb->dvbq.threadio) {
4862306a36Sopenharmony_ci		rc = vb2_thread_start(&dvb->dvbq, dvb_fnc, dvb, dvb->name);
4962306a36Sopenharmony_ci		if (rc)
5062306a36Sopenharmony_ci			dvb->nfeeds--;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci	if (!rc)
5362306a36Sopenharmony_ci		rc = dvb->nfeeds;
5462306a36Sopenharmony_ci	mutex_unlock(&dvb->lock);
5562306a36Sopenharmony_ci	return rc;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int vb2_dvb_stop_feed(struct dvb_demux_feed *feed)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
6162306a36Sopenharmony_ci	struct vb2_dvb *dvb = demux->priv;
6262306a36Sopenharmony_ci	int err = 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mutex_lock(&dvb->lock);
6562306a36Sopenharmony_ci	dvb->nfeeds--;
6662306a36Sopenharmony_ci	if (0 == dvb->nfeeds)
6762306a36Sopenharmony_ci		err = vb2_thread_stop(&dvb->dvbq);
6862306a36Sopenharmony_ci	mutex_unlock(&dvb->lock);
6962306a36Sopenharmony_ci	return err;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int vb2_dvb_register_adapter(struct vb2_dvb_frontends *fe,
7362306a36Sopenharmony_ci			  struct module *module,
7462306a36Sopenharmony_ci			  void *adapter_priv,
7562306a36Sopenharmony_ci			  struct device *device,
7662306a36Sopenharmony_ci			  struct media_device *mdev,
7762306a36Sopenharmony_ci			  char *adapter_name,
7862306a36Sopenharmony_ci			  short *adapter_nr,
7962306a36Sopenharmony_ci			  int mfe_shared)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	int result;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	mutex_init(&fe->lock);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* register adapter */
8662306a36Sopenharmony_ci	result = dvb_register_adapter(&fe->adapter, adapter_name, module,
8762306a36Sopenharmony_ci		device, adapter_nr);
8862306a36Sopenharmony_ci	if (result < 0) {
8962306a36Sopenharmony_ci		pr_warn("%s: dvb_register_adapter failed (errno = %d)\n",
9062306a36Sopenharmony_ci		       adapter_name, result);
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	fe->adapter.priv = adapter_priv;
9362306a36Sopenharmony_ci	fe->adapter.mfe_shared = mfe_shared;
9462306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
9562306a36Sopenharmony_ci	if (mdev)
9662306a36Sopenharmony_ci		fe->adapter.mdev = mdev;
9762306a36Sopenharmony_ci#endif
9862306a36Sopenharmony_ci	return result;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int vb2_dvb_register_frontend(struct dvb_adapter *adapter,
10262306a36Sopenharmony_ci	struct vb2_dvb *dvb)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	int result;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* register frontend */
10762306a36Sopenharmony_ci	result = dvb_register_frontend(adapter, dvb->frontend);
10862306a36Sopenharmony_ci	if (result < 0) {
10962306a36Sopenharmony_ci		pr_warn("%s: dvb_register_frontend failed (errno = %d)\n",
11062306a36Sopenharmony_ci		       dvb->name, result);
11162306a36Sopenharmony_ci		goto fail_frontend;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* register demux stuff */
11562306a36Sopenharmony_ci	dvb->demux.dmx.capabilities =
11662306a36Sopenharmony_ci		DMX_TS_FILTERING | DMX_SECTION_FILTERING |
11762306a36Sopenharmony_ci		DMX_MEMORY_BASED_FILTERING;
11862306a36Sopenharmony_ci	dvb->demux.priv       = dvb;
11962306a36Sopenharmony_ci	dvb->demux.filternum  = 256;
12062306a36Sopenharmony_ci	dvb->demux.feednum    = 256;
12162306a36Sopenharmony_ci	dvb->demux.start_feed = vb2_dvb_start_feed;
12262306a36Sopenharmony_ci	dvb->demux.stop_feed  = vb2_dvb_stop_feed;
12362306a36Sopenharmony_ci	result = dvb_dmx_init(&dvb->demux);
12462306a36Sopenharmony_ci	if (result < 0) {
12562306a36Sopenharmony_ci		pr_warn("%s: dvb_dmx_init failed (errno = %d)\n",
12662306a36Sopenharmony_ci		       dvb->name, result);
12762306a36Sopenharmony_ci		goto fail_dmx;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	dvb->dmxdev.filternum    = 256;
13162306a36Sopenharmony_ci	dvb->dmxdev.demux        = &dvb->demux.dmx;
13262306a36Sopenharmony_ci	dvb->dmxdev.capabilities = 0;
13362306a36Sopenharmony_ci	result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (result < 0) {
13662306a36Sopenharmony_ci		pr_warn("%s: dvb_dmxdev_init failed (errno = %d)\n",
13762306a36Sopenharmony_ci		       dvb->name, result);
13862306a36Sopenharmony_ci		goto fail_dmxdev;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	dvb->fe_hw.source = DMX_FRONTEND_0;
14262306a36Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
14362306a36Sopenharmony_ci	if (result < 0) {
14462306a36Sopenharmony_ci		pr_warn("%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
14562306a36Sopenharmony_ci		       dvb->name, result);
14662306a36Sopenharmony_ci		goto fail_fe_hw;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	dvb->fe_mem.source = DMX_MEMORY_FE;
15062306a36Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
15162306a36Sopenharmony_ci	if (result < 0) {
15262306a36Sopenharmony_ci		pr_warn("%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
15362306a36Sopenharmony_ci		       dvb->name, result);
15462306a36Sopenharmony_ci		goto fail_fe_mem;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
15862306a36Sopenharmony_ci	if (result < 0) {
15962306a36Sopenharmony_ci		pr_warn("%s: connect_frontend failed (errno = %d)\n",
16062306a36Sopenharmony_ci		       dvb->name, result);
16162306a36Sopenharmony_ci		goto fail_fe_conn;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* register network adapter */
16562306a36Sopenharmony_ci	result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
16662306a36Sopenharmony_ci	if (result < 0) {
16762306a36Sopenharmony_ci		pr_warn("%s: dvb_net_init failed (errno = %d)\n",
16862306a36Sopenharmony_ci		       dvb->name, result);
16962306a36Sopenharmony_ci		goto fail_fe_conn;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cifail_fe_conn:
17462306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
17562306a36Sopenharmony_cifail_fe_mem:
17662306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
17762306a36Sopenharmony_cifail_fe_hw:
17862306a36Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
17962306a36Sopenharmony_cifail_dmxdev:
18062306a36Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
18162306a36Sopenharmony_cifail_dmx:
18262306a36Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend);
18362306a36Sopenharmony_cifail_frontend:
18462306a36Sopenharmony_ci	dvb_frontend_detach(dvb->frontend);
18562306a36Sopenharmony_ci	dvb->frontend = NULL;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return result;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
19162306a36Sopenharmony_ci/* Register a single adapter and one or more frontends */
19262306a36Sopenharmony_ciint vb2_dvb_register_bus(struct vb2_dvb_frontends *f,
19362306a36Sopenharmony_ci			 struct module *module,
19462306a36Sopenharmony_ci			 void *adapter_priv,
19562306a36Sopenharmony_ci			 struct device *device,
19662306a36Sopenharmony_ci			 struct media_device *mdev,
19762306a36Sopenharmony_ci			 short *adapter_nr,
19862306a36Sopenharmony_ci			 int mfe_shared)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct list_head *list, *q;
20162306a36Sopenharmony_ci	struct vb2_dvb_frontend *fe;
20262306a36Sopenharmony_ci	int res;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	fe = vb2_dvb_get_frontend(f, 1);
20562306a36Sopenharmony_ci	if (!fe) {
20662306a36Sopenharmony_ci		pr_warn("Unable to register the adapter which has no frontends\n");
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Bring up the adapter */
21162306a36Sopenharmony_ci	res = vb2_dvb_register_adapter(f, module, adapter_priv, device, mdev,
21262306a36Sopenharmony_ci		fe->dvb.name, adapter_nr, mfe_shared);
21362306a36Sopenharmony_ci	if (res < 0) {
21462306a36Sopenharmony_ci		pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n", res);
21562306a36Sopenharmony_ci		return res;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* Attach all of the frontends to the adapter */
21962306a36Sopenharmony_ci	mutex_lock(&f->lock);
22062306a36Sopenharmony_ci	list_for_each_safe(list, q, &f->felist) {
22162306a36Sopenharmony_ci		fe = list_entry(list, struct vb2_dvb_frontend, felist);
22262306a36Sopenharmony_ci		res = vb2_dvb_register_frontend(&f->adapter, &fe->dvb);
22362306a36Sopenharmony_ci		if (res < 0) {
22462306a36Sopenharmony_ci			pr_warn("%s: vb2_dvb_register_frontend failed (errno = %d)\n",
22562306a36Sopenharmony_ci				fe->dvb.name, res);
22662306a36Sopenharmony_ci			goto err;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci		res = dvb_create_media_graph(&f->adapter, false);
22962306a36Sopenharmony_ci		if (res < 0)
23062306a36Sopenharmony_ci			goto err;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	mutex_unlock(&f->lock);
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cierr:
23762306a36Sopenharmony_ci	mutex_unlock(&f->lock);
23862306a36Sopenharmony_ci	vb2_dvb_unregister_bus(f);
23962306a36Sopenharmony_ci	return res;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ciEXPORT_SYMBOL(vb2_dvb_register_bus);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_civoid vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	vb2_dvb_dealloc_frontends(f);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dvb_unregister_adapter(&f->adapter);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ciEXPORT_SYMBOL(vb2_dvb_unregister_bus);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistruct vb2_dvb_frontend *vb2_dvb_get_frontend(
25262306a36Sopenharmony_ci	struct vb2_dvb_frontends *f, int id)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct list_head *list, *q;
25562306a36Sopenharmony_ci	struct vb2_dvb_frontend *fe, *ret = NULL;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	mutex_lock(&f->lock);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	list_for_each_safe(list, q, &f->felist) {
26062306a36Sopenharmony_ci		fe = list_entry(list, struct vb2_dvb_frontend, felist);
26162306a36Sopenharmony_ci		if (fe->id == id) {
26262306a36Sopenharmony_ci			ret = fe;
26362306a36Sopenharmony_ci			break;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	mutex_unlock(&f->lock);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return ret;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ciEXPORT_SYMBOL(vb2_dvb_get_frontend);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ciint vb2_dvb_find_frontend(struct vb2_dvb_frontends *f,
27462306a36Sopenharmony_ci	struct dvb_frontend *p)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct list_head *list, *q;
27762306a36Sopenharmony_ci	struct vb2_dvb_frontend *fe = NULL;
27862306a36Sopenharmony_ci	int ret = 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	mutex_lock(&f->lock);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	list_for_each_safe(list, q, &f->felist) {
28362306a36Sopenharmony_ci		fe = list_entry(list, struct vb2_dvb_frontend, felist);
28462306a36Sopenharmony_ci		if (fe->dvb.frontend == p) {
28562306a36Sopenharmony_ci			ret = fe->id;
28662306a36Sopenharmony_ci			break;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	mutex_unlock(&f->lock);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return ret;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ciEXPORT_SYMBOL(vb2_dvb_find_frontend);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistruct vb2_dvb_frontend *vb2_dvb_alloc_frontend(
29762306a36Sopenharmony_ci	struct vb2_dvb_frontends *f, int id)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct vb2_dvb_frontend *fe;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	fe = kzalloc(sizeof(struct vb2_dvb_frontend), GFP_KERNEL);
30262306a36Sopenharmony_ci	if (fe == NULL)
30362306a36Sopenharmony_ci		return NULL;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	fe->id = id;
30662306a36Sopenharmony_ci	mutex_init(&fe->dvb.lock);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	mutex_lock(&f->lock);
30962306a36Sopenharmony_ci	list_add_tail(&fe->felist, &f->felist);
31062306a36Sopenharmony_ci	mutex_unlock(&f->lock);
31162306a36Sopenharmony_ci	return fe;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ciEXPORT_SYMBOL(vb2_dvb_alloc_frontend);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_civoid vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct list_head *list, *q;
31862306a36Sopenharmony_ci	struct vb2_dvb_frontend *fe;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	mutex_lock(&f->lock);
32162306a36Sopenharmony_ci	list_for_each_safe(list, q, &f->felist) {
32262306a36Sopenharmony_ci		fe = list_entry(list, struct vb2_dvb_frontend, felist);
32362306a36Sopenharmony_ci		if (fe->dvb.net.dvbdev) {
32462306a36Sopenharmony_ci			dvb_net_release(&fe->dvb.net);
32562306a36Sopenharmony_ci			fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
32662306a36Sopenharmony_ci				&fe->dvb.fe_mem);
32762306a36Sopenharmony_ci			fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
32862306a36Sopenharmony_ci				&fe->dvb.fe_hw);
32962306a36Sopenharmony_ci			dvb_dmxdev_release(&fe->dvb.dmxdev);
33062306a36Sopenharmony_ci			dvb_dmx_release(&fe->dvb.demux);
33162306a36Sopenharmony_ci			dvb_unregister_frontend(fe->dvb.frontend);
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		if (fe->dvb.frontend)
33462306a36Sopenharmony_ci			/* always allocated, may have been reset */
33562306a36Sopenharmony_ci			dvb_frontend_detach(fe->dvb.frontend);
33662306a36Sopenharmony_ci		list_del(list); /* remove list entry */
33762306a36Sopenharmony_ci		kfree(fe);	/* free frontend allocation */
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci	mutex_unlock(&f->lock);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ciEXPORT_SYMBOL(vb2_dvb_dealloc_frontends);
342