151 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ALSA interface to cobalt PCM capture streams4 *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 25static 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 35static 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 44static 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 61static 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 strscpy(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 82int 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 135err_exit_free:136 if (sc != NULL)137 snd_card_free(sc);138 kfree(cobsc);139err_exit:140 return ret;141}142 143void 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