18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA interface to cobalt PCM capture streams 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. 68c2ecf20Sopenharmony_ci * All rights reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <sound/core.h> 198c2ecf20Sopenharmony_ci#include <sound/initval.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "cobalt-driver.h" 228c2ecf20Sopenharmony_ci#include "cobalt-alsa.h" 238c2ecf20Sopenharmony_ci#include "cobalt-alsa-pcm.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void snd_cobalt_card_free(struct snd_cobalt_card *cobsc) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci if (cobsc == NULL) 288c2ecf20Sopenharmony_ci return; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci cobsc->s->alsa = NULL; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci kfree(cobsc); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void snd_cobalt_card_private_free(struct snd_card *sc) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci if (sc == NULL) 388c2ecf20Sopenharmony_ci return; 398c2ecf20Sopenharmony_ci snd_cobalt_card_free(sc->private_data); 408c2ecf20Sopenharmony_ci sc->private_data = NULL; 418c2ecf20Sopenharmony_ci sc->private_free = NULL; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int snd_cobalt_card_create(struct cobalt_stream *s, 458c2ecf20Sopenharmony_ci struct snd_card *sc, 468c2ecf20Sopenharmony_ci struct snd_cobalt_card **cobsc) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci *cobsc = kzalloc(sizeof(struct snd_cobalt_card), GFP_KERNEL); 498c2ecf20Sopenharmony_ci if (*cobsc == NULL) 508c2ecf20Sopenharmony_ci return -ENOMEM; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci (*cobsc)->s = s; 538c2ecf20Sopenharmony_ci (*cobsc)->sc = sc; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci sc->private_data = *cobsc; 568c2ecf20Sopenharmony_ci sc->private_free = snd_cobalt_card_private_free; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int snd_cobalt_card_set_names(struct snd_cobalt_card *cobsc) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct cobalt_stream *s = cobsc->s; 648c2ecf20Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 658c2ecf20Sopenharmony_ci struct snd_card *sc = cobsc->sc; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* sc->driver is used by alsa-lib's configurator: simple, unique */ 688c2ecf20Sopenharmony_ci strscpy(sc->driver, "cobalt", sizeof(sc->driver)); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* sc->shortname is a symlink in /proc/asound: COBALT-M -> cardN */ 718c2ecf20Sopenharmony_ci snprintf(sc->shortname, sizeof(sc->shortname), "cobalt-%d-%d", 728c2ecf20Sopenharmony_ci cobalt->instance, s->video_channel); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* sc->longname is read from /proc/asound/cards */ 758c2ecf20Sopenharmony_ci snprintf(sc->longname, sizeof(sc->longname), 768c2ecf20Sopenharmony_ci "Cobalt %d HDMI %d", 778c2ecf20Sopenharmony_ci cobalt->instance, s->video_channel); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciint cobalt_alsa_init(struct cobalt_stream *s) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 858c2ecf20Sopenharmony_ci struct snd_card *sc = NULL; 868c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc; 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* (1) Check and increment the device index */ 928c2ecf20Sopenharmony_ci /* This is a no-op for us. We'll use the cobalt->instance */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* (2) Create a card instance */ 958c2ecf20Sopenharmony_ci ret = snd_card_new(&cobalt->pci_dev->dev, SNDRV_DEFAULT_IDX1, 968c2ecf20Sopenharmony_ci SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &sc); 978c2ecf20Sopenharmony_ci if (ret) { 988c2ecf20Sopenharmony_ci cobalt_err("snd_card_new() failed with err %d\n", ret); 998c2ecf20Sopenharmony_ci goto err_exit; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* (3) Create a main component */ 1038c2ecf20Sopenharmony_ci ret = snd_cobalt_card_create(s, sc, &cobsc); 1048c2ecf20Sopenharmony_ci if (ret) { 1058c2ecf20Sopenharmony_ci cobalt_err("snd_cobalt_card_create() failed with err %d\n", 1068c2ecf20Sopenharmony_ci ret); 1078c2ecf20Sopenharmony_ci goto err_exit_free; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* (4) Set the driver ID and name strings */ 1118c2ecf20Sopenharmony_ci snd_cobalt_card_set_names(cobsc); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = snd_cobalt_pcm_create(cobsc); 1148c2ecf20Sopenharmony_ci if (ret) { 1158c2ecf20Sopenharmony_ci cobalt_err("snd_cobalt_pcm_create() failed with err %d\n", 1168c2ecf20Sopenharmony_ci ret); 1178c2ecf20Sopenharmony_ci goto err_exit_free; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci /* FIXME - proc files */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* (7) Set the driver data and return 0 */ 1228c2ecf20Sopenharmony_ci /* We do this out of normal order for PCI drivers to avoid races */ 1238c2ecf20Sopenharmony_ci s->alsa = cobsc; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* (6) Register the card instance */ 1268c2ecf20Sopenharmony_ci ret = snd_card_register(sc); 1278c2ecf20Sopenharmony_ci if (ret) { 1288c2ecf20Sopenharmony_ci s->alsa = NULL; 1298c2ecf20Sopenharmony_ci cobalt_err("snd_card_register() failed with err %d\n", ret); 1308c2ecf20Sopenharmony_ci goto err_exit_free; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cierr_exit_free: 1368c2ecf20Sopenharmony_ci if (sc != NULL) 1378c2ecf20Sopenharmony_ci snd_card_free(sc); 1388c2ecf20Sopenharmony_ci kfree(cobsc); 1398c2ecf20Sopenharmony_cierr_exit: 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_civoid cobalt_alsa_exit(struct cobalt_stream *s) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = s->alsa; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (cobsc) 1488c2ecf20Sopenharmony_ci snd_card_free(cobsc->sc); 1498c2ecf20Sopenharmony_ci s->alsa = NULL; 1508c2ecf20Sopenharmony_ci} 151