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