1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ALSA interface to cobalt PCM capture streams 4 * 5 * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. 6 * All rights reserved. 7 */ 8 9 #include <linux/init.h> 10 #include <linux/slab.h> 11 #include <linux/module.h> 12 #include <linux/kernel.h> 13 #include <linux/device.h> 14 #include <linux/spinlock.h> 15 16 #include <media/v4l2-device.h> 17 18 #include <sound/core.h> 19 #include <sound/initval.h> 20 21 #include "cobalt-driver.h" 22 #include "cobalt-alsa.h" 23 #include "cobalt-alsa-pcm.h" 24 25 static void snd_cobalt_card_free(struct snd_cobalt_card *cobsc) 26 { 27 if (cobsc == NULL) 28 return; 29 30 cobsc->s->alsa = NULL; 31 32 kfree(cobsc); 33 } 34 35 static void snd_cobalt_card_private_free(struct snd_card *sc) 36 { 37 if (sc == NULL) 38 return; 39 snd_cobalt_card_free(sc->private_data); 40 sc->private_data = NULL; 41 sc->private_free = NULL; 42 } 43 44 static int snd_cobalt_card_create(struct cobalt_stream *s, 45 struct snd_card *sc, 46 struct snd_cobalt_card **cobsc) 47 { 48 *cobsc = kzalloc(sizeof(struct snd_cobalt_card), GFP_KERNEL); 49 if (*cobsc == NULL) 50 return -ENOMEM; 51 52 (*cobsc)->s = s; 53 (*cobsc)->sc = sc; 54 55 sc->private_data = *cobsc; 56 sc->private_free = snd_cobalt_card_private_free; 57 58 return 0; 59 } 60 61 static int snd_cobalt_card_set_names(struct snd_cobalt_card *cobsc) 62 { 63 struct cobalt_stream *s = cobsc->s; 64 struct cobalt *cobalt = s->cobalt; 65 struct snd_card *sc = cobsc->sc; 66 67 /* sc->driver is used by alsa-lib's configurator: simple, unique */ 68 strlcpy(sc->driver, "cobalt", sizeof(sc->driver)); 69 70 /* sc->shortname is a symlink in /proc/asound: COBALT-M -> cardN */ 71 snprintf(sc->shortname, sizeof(sc->shortname), "cobalt-%d-%d", 72 cobalt->instance, s->video_channel); 73 74 /* sc->longname is read from /proc/asound/cards */ 75 snprintf(sc->longname, sizeof(sc->longname), 76 "Cobalt %d HDMI %d", 77 cobalt->instance, s->video_channel); 78 79 return 0; 80 } 81 82 int cobalt_alsa_init(struct cobalt_stream *s) 83 { 84 struct cobalt *cobalt = s->cobalt; 85 struct snd_card *sc = NULL; 86 struct snd_cobalt_card *cobsc; 87 int ret; 88 89 /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ 90 91 /* (1) Check and increment the device index */ 92 /* This is a no-op for us. We'll use the cobalt->instance */ 93 94 /* (2) Create a card instance */ 95 ret = snd_card_new(&cobalt->pci_dev->dev, SNDRV_DEFAULT_IDX1, 96 SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &sc); 97 if (ret) { 98 cobalt_err("snd_card_new() failed with err %d\n", ret); 99 goto err_exit; 100 } 101 102 /* (3) Create a main component */ 103 ret = snd_cobalt_card_create(s, sc, &cobsc); 104 if (ret) { 105 cobalt_err("snd_cobalt_card_create() failed with err %d\n", 106 ret); 107 goto err_exit_free; 108 } 109 110 /* (4) Set the driver ID and name strings */ 111 snd_cobalt_card_set_names(cobsc); 112 113 ret = snd_cobalt_pcm_create(cobsc); 114 if (ret) { 115 cobalt_err("snd_cobalt_pcm_create() failed with err %d\n", 116 ret); 117 goto err_exit_free; 118 } 119 /* FIXME - proc files */ 120 121 /* (7) Set the driver data and return 0 */ 122 /* We do this out of normal order for PCI drivers to avoid races */ 123 s->alsa = cobsc; 124 125 /* (6) Register the card instance */ 126 ret = snd_card_register(sc); 127 if (ret) { 128 s->alsa = NULL; 129 cobalt_err("snd_card_register() failed with err %d\n", ret); 130 goto err_exit_free; 131 } 132 133 return 0; 134 135 err_exit_free: 136 if (sc != NULL) 137 snd_card_free(sc); 138 kfree(cobsc); 139 err_exit: 140 return ret; 141 } 142 143 void cobalt_alsa_exit(struct cobalt_stream *s) 144 { 145 struct snd_cobalt_card *cobsc = s->alsa; 146 147 if (cobsc) 148 snd_card_free(cobsc->sc); 149 s->alsa = NULL; 150 } 151