16884db3cSHans Verkuil // SPDX-License-Identifier: GPL-2.0-only
285756a06SHans Verkuil /*
385756a06SHans Verkuil  *  ALSA interface to cobalt PCM capture streams
485756a06SHans Verkuil  *
585756a06SHans Verkuil  *  Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
685756a06SHans Verkuil  *  All rights reserved.
785756a06SHans Verkuil  */
885756a06SHans Verkuil 
985756a06SHans Verkuil #include <linux/init.h>
1085756a06SHans Verkuil #include <linux/slab.h>
1185756a06SHans Verkuil #include <linux/module.h>
1285756a06SHans Verkuil #include <linux/kernel.h>
1385756a06SHans Verkuil #include <linux/device.h>
1485756a06SHans Verkuil #include <linux/spinlock.h>
1585756a06SHans Verkuil 
1685756a06SHans Verkuil #include <media/v4l2-device.h>
1785756a06SHans Verkuil 
1885756a06SHans Verkuil #include <sound/core.h>
1985756a06SHans Verkuil #include <sound/initval.h>
2085756a06SHans Verkuil 
2185756a06SHans Verkuil #include "cobalt-driver.h"
2285756a06SHans Verkuil #include "cobalt-alsa.h"
2385756a06SHans Verkuil #include "cobalt-alsa-pcm.h"
2485756a06SHans Verkuil 
snd_cobalt_card_free(struct snd_cobalt_card * cobsc)2585756a06SHans Verkuil static void snd_cobalt_card_free(struct snd_cobalt_card *cobsc)
2685756a06SHans Verkuil {
2785756a06SHans Verkuil 	if (cobsc == NULL)
2885756a06SHans Verkuil 		return;
2985756a06SHans Verkuil 
3085756a06SHans Verkuil 	cobsc->s->alsa = NULL;
3185756a06SHans Verkuil 
3285756a06SHans Verkuil 	kfree(cobsc);
3385756a06SHans Verkuil }
3485756a06SHans Verkuil 
snd_cobalt_card_private_free(struct snd_card * sc)3585756a06SHans Verkuil static void snd_cobalt_card_private_free(struct snd_card *sc)
3685756a06SHans Verkuil {
3785756a06SHans Verkuil 	if (sc == NULL)
3885756a06SHans Verkuil 		return;
3985756a06SHans Verkuil 	snd_cobalt_card_free(sc->private_data);
4085756a06SHans Verkuil 	sc->private_data = NULL;
4185756a06SHans Verkuil 	sc->private_free = NULL;
4285756a06SHans Verkuil }
4385756a06SHans Verkuil 
snd_cobalt_card_create(struct cobalt_stream * s,struct snd_card * sc,struct snd_cobalt_card ** cobsc)4485756a06SHans Verkuil static int snd_cobalt_card_create(struct cobalt_stream *s,
4585756a06SHans Verkuil 				       struct snd_card *sc,
4685756a06SHans Verkuil 				       struct snd_cobalt_card **cobsc)
4785756a06SHans Verkuil {
4885756a06SHans Verkuil 	*cobsc = kzalloc(sizeof(struct snd_cobalt_card), GFP_KERNEL);
4985756a06SHans Verkuil 	if (*cobsc == NULL)
5085756a06SHans Verkuil 		return -ENOMEM;
5185756a06SHans Verkuil 
5285756a06SHans Verkuil 	(*cobsc)->s = s;
5385756a06SHans Verkuil 	(*cobsc)->sc = sc;
5485756a06SHans Verkuil 
5585756a06SHans Verkuil 	sc->private_data = *cobsc;
5685756a06SHans Verkuil 	sc->private_free = snd_cobalt_card_private_free;
5785756a06SHans Verkuil 
5885756a06SHans Verkuil 	return 0;
5985756a06SHans Verkuil }
6085756a06SHans Verkuil 
snd_cobalt_card_set_names(struct snd_cobalt_card * cobsc)6185756a06SHans Verkuil static int snd_cobalt_card_set_names(struct snd_cobalt_card *cobsc)
6285756a06SHans Verkuil {
6385756a06SHans Verkuil 	struct cobalt_stream *s = cobsc->s;
6485756a06SHans Verkuil 	struct cobalt *cobalt = s->cobalt;
6585756a06SHans Verkuil 	struct snd_card *sc = cobsc->sc;
6685756a06SHans Verkuil 
6785756a06SHans Verkuil 	/* sc->driver is used by alsa-lib's configurator: simple, unique */
68c0decac1SMauro Carvalho Chehab 	strscpy(sc->driver, "cobalt", sizeof(sc->driver));
6985756a06SHans Verkuil 
7085756a06SHans Verkuil 	/* sc->shortname is a symlink in /proc/asound: COBALT-M -> cardN */
7185756a06SHans Verkuil 	snprintf(sc->shortname,  sizeof(sc->shortname), "cobalt-%d-%d",
7285756a06SHans Verkuil 		 cobalt->instance, s->video_channel);
7385756a06SHans Verkuil 
7485756a06SHans Verkuil 	/* sc->longname is read from /proc/asound/cards */
7585756a06SHans Verkuil 	snprintf(sc->longname, sizeof(sc->longname),
7685756a06SHans Verkuil 		 "Cobalt %d HDMI %d",
7785756a06SHans Verkuil 		 cobalt->instance, s->video_channel);
7885756a06SHans Verkuil 
7985756a06SHans Verkuil 	return 0;
8085756a06SHans Verkuil }
8185756a06SHans Verkuil 
cobalt_alsa_init(struct cobalt_stream * s)8285756a06SHans Verkuil int cobalt_alsa_init(struct cobalt_stream *s)
8385756a06SHans Verkuil {
8485756a06SHans Verkuil 	struct cobalt *cobalt = s->cobalt;
8585756a06SHans Verkuil 	struct snd_card *sc = NULL;
8685756a06SHans Verkuil 	struct snd_cobalt_card *cobsc;
8785756a06SHans Verkuil 	int ret;
8885756a06SHans Verkuil 
8985756a06SHans Verkuil 	/* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
9085756a06SHans Verkuil 
9185756a06SHans Verkuil 	/* (1) Check and increment the device index */
9285756a06SHans Verkuil 	/* This is a no-op for us.  We'll use the cobalt->instance */
9385756a06SHans Verkuil 
9485756a06SHans Verkuil 	/* (2) Create a card instance */
9585756a06SHans Verkuil 	ret = snd_card_new(&cobalt->pci_dev->dev, SNDRV_DEFAULT_IDX1,
9685756a06SHans Verkuil 			   SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &sc);
9785756a06SHans Verkuil 	if (ret) {
9885756a06SHans Verkuil 		cobalt_err("snd_card_new() failed with err %d\n", ret);
9985756a06SHans Verkuil 		goto err_exit;
10085756a06SHans Verkuil 	}
10185756a06SHans Verkuil 
10285756a06SHans Verkuil 	/* (3) Create a main component */
10385756a06SHans Verkuil 	ret = snd_cobalt_card_create(s, sc, &cobsc);
10485756a06SHans Verkuil 	if (ret) {
10585756a06SHans Verkuil 		cobalt_err("snd_cobalt_card_create() failed with err %d\n",
10685756a06SHans Verkuil 			   ret);
10785756a06SHans Verkuil 		goto err_exit_free;
10885756a06SHans Verkuil 	}
10985756a06SHans Verkuil 
11085756a06SHans Verkuil 	/* (4) Set the driver ID and name strings */
11185756a06SHans Verkuil 	snd_cobalt_card_set_names(cobsc);
11285756a06SHans Verkuil 
11385756a06SHans Verkuil 	ret = snd_cobalt_pcm_create(cobsc);
11485756a06SHans Verkuil 	if (ret) {
11585756a06SHans Verkuil 		cobalt_err("snd_cobalt_pcm_create() failed with err %d\n",
11685756a06SHans Verkuil 			   ret);
11785756a06SHans Verkuil 		goto err_exit_free;
11885756a06SHans Verkuil 	}
11985756a06SHans Verkuil 	/* FIXME - proc files */
12085756a06SHans Verkuil 
12185756a06SHans Verkuil 	/* (7) Set the driver data and return 0 */
12285756a06SHans Verkuil 	/* We do this out of normal order for PCI drivers to avoid races */
12385756a06SHans Verkuil 	s->alsa = cobsc;
12485756a06SHans Verkuil 
12585756a06SHans Verkuil 	/* (6) Register the card instance */
12685756a06SHans Verkuil 	ret = snd_card_register(sc);
12785756a06SHans Verkuil 	if (ret) {
12885756a06SHans Verkuil 		s->alsa = NULL;
12985756a06SHans Verkuil 		cobalt_err("snd_card_register() failed with err %d\n", ret);
13085756a06SHans Verkuil 		goto err_exit_free;
13185756a06SHans Verkuil 	}
13285756a06SHans Verkuil 
13385756a06SHans Verkuil 	return 0;
13485756a06SHans Verkuil 
13585756a06SHans Verkuil err_exit_free:
13685756a06SHans Verkuil 	if (sc != NULL)
13785756a06SHans Verkuil 		snd_card_free(sc);
13885756a06SHans Verkuil 	kfree(cobsc);
13985756a06SHans Verkuil err_exit:
14085756a06SHans Verkuil 	return ret;
14185756a06SHans Verkuil }
14285756a06SHans Verkuil 
cobalt_alsa_exit(struct cobalt_stream * s)14385756a06SHans Verkuil void cobalt_alsa_exit(struct cobalt_stream *s)
14485756a06SHans Verkuil {
14585756a06SHans Verkuil 	struct snd_cobalt_card *cobsc = s->alsa;
14685756a06SHans Verkuil 
14785756a06SHans Verkuil 	if (cobsc)
14885756a06SHans Verkuil 		snd_card_free(cobsc->sc);
14985756a06SHans Verkuil 	s->alsa = NULL;
15085756a06SHans Verkuil }
151