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 */
68*c0decac1SMauro 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