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 37static 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 47static 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 56static 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 73static 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 94int 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 147err_exit_free: 148 if (sc != NULL) 149 snd_card_free(sc); 150 kfree(cobsc); 151err_exit: 152 return ret; 153} 154 155void 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