1 /* 2 * ALSA interface to cobalt PCM capture streams 3 * 4 * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. 5 * All rights reserved. 6 * 7 * This program is free software; you may redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; version 2 of the License. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 12 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 15 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 16 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 * SOFTWARE. 19 */ 20 21 #include <linux/init.h> 22 #include <linux/slab.h> 23 #include <linux/module.h> 24 #include <linux/kernel.h> 25 #include <linux/device.h> 26 #include <linux/spinlock.h> 27 28 #include <media/v4l2-device.h> 29 30 #include <sound/core.h> 31 #include <sound/initval.h> 32 33 #include "cobalt-driver.h" 34 #include "cobalt-alsa.h" 35 #include "cobalt-alsa-pcm.h" 36 37 static void snd_cobalt_card_free(struct snd_cobalt_card *cobsc) 38 { 39 if (cobsc == NULL) 40 return; 41 42 cobsc->s->alsa = NULL; 43 44 kfree(cobsc); 45 } 46 47 static void snd_cobalt_card_private_free(struct snd_card *sc) 48 { 49 if (sc == NULL) 50 return; 51 snd_cobalt_card_free(sc->private_data); 52 sc->private_data = NULL; 53 sc->private_free = NULL; 54 } 55 56 static int snd_cobalt_card_create(struct cobalt_stream *s, 57 struct snd_card *sc, 58 struct snd_cobalt_card **cobsc) 59 { 60 *cobsc = kzalloc(sizeof(struct snd_cobalt_card), GFP_KERNEL); 61 if (*cobsc == NULL) 62 return -ENOMEM; 63 64 (*cobsc)->s = s; 65 (*cobsc)->sc = sc; 66 67 sc->private_data = *cobsc; 68 sc->private_free = snd_cobalt_card_private_free; 69 70 return 0; 71 } 72 73 static int snd_cobalt_card_set_names(struct snd_cobalt_card *cobsc) 74 { 75 struct cobalt_stream *s = cobsc->s; 76 struct cobalt *cobalt = s->cobalt; 77 struct snd_card *sc = cobsc->sc; 78 79 /* sc->driver is used by alsa-lib's configurator: simple, unique */ 80 strlcpy(sc->driver, "cobalt", sizeof(sc->driver)); 81 82 /* sc->shortname is a symlink in /proc/asound: COBALT-M -> cardN */ 83 snprintf(sc->shortname, sizeof(sc->shortname), "cobalt-%d-%d", 84 cobalt->instance, s->video_channel); 85 86 /* sc->longname is read from /proc/asound/cards */ 87 snprintf(sc->longname, sizeof(sc->longname), 88 "Cobalt %d HDMI %d", 89 cobalt->instance, s->video_channel); 90 91 return 0; 92 } 93 94 int cobalt_alsa_init(struct cobalt_stream *s) 95 { 96 struct cobalt *cobalt = s->cobalt; 97 struct snd_card *sc = NULL; 98 struct snd_cobalt_card *cobsc; 99 int ret; 100 101 /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ 102 103 /* (1) Check and increment the device index */ 104 /* This is a no-op for us. We'll use the cobalt->instance */ 105 106 /* (2) Create a card instance */ 107 ret = snd_card_new(&cobalt->pci_dev->dev, SNDRV_DEFAULT_IDX1, 108 SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &sc); 109 if (ret) { 110 cobalt_err("snd_card_new() failed with err %d\n", ret); 111 goto err_exit; 112 } 113 114 /* (3) Create a main component */ 115 ret = snd_cobalt_card_create(s, sc, &cobsc); 116 if (ret) { 117 cobalt_err("snd_cobalt_card_create() failed with err %d\n", 118 ret); 119 goto err_exit_free; 120 } 121 122 /* (4) Set the driver ID and name strings */ 123 snd_cobalt_card_set_names(cobsc); 124 125 ret = snd_cobalt_pcm_create(cobsc); 126 if (ret) { 127 cobalt_err("snd_cobalt_pcm_create() failed with err %d\n", 128 ret); 129 goto err_exit_free; 130 } 131 /* FIXME - proc files */ 132 133 /* (7) Set the driver data and return 0 */ 134 /* We do this out of normal order for PCI drivers to avoid races */ 135 s->alsa = cobsc; 136 137 /* (6) Register the card instance */ 138 ret = snd_card_register(sc); 139 if (ret) { 140 s->alsa = NULL; 141 cobalt_err("snd_card_register() failed with err %d\n", ret); 142 goto err_exit_free; 143 } 144 145 return 0; 146 147 err_exit_free: 148 if (sc != NULL) 149 snd_card_free(sc); 150 kfree(cobsc); 151 err_exit: 152 return ret; 153 } 154 155 void cobalt_alsa_exit(struct cobalt_stream *s) 156 { 157 struct snd_cobalt_card *cobsc = s->alsa; 158 159 if (cobsc) 160 snd_card_free(cobsc->sc); 161 s->alsa = NULL; 162 } 163