simple-scu-card.c 9.2 KB
Newer Older
1
/*
2
 * ASoC simple SCU sound card support
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * Copyright (C) 2015 Renesas Solutions Corp.
 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 *
 * based on ${LINUX}/sound/soc/generic/simple-card.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
23
#include <sound/simple_card_utils.h>
24

25
struct asoc_simple_card_of_data {
26 27 28 29 30
	const char *prefix;
	const struct snd_soc_dapm_route *routes;
	int num_routes;
};

31
static const struct of_device_id asoc_simple_card_of_match[] = {
32
	{ .compatible = "renesas,rsrc-card", },
33
	{ .compatible = "simple-scu-audio-card", },
34 35
	{},
};
36
MODULE_DEVICE_TABLE(of, asoc_simple_card_of_match);
37 38 39

#define IDX_CPU		0
#define IDX_CODEC	1
40
struct asoc_simple_card_priv {
41 42
	struct snd_soc_card snd_card;
	struct snd_soc_codec_conf codec_conf;
43
	struct asoc_simple_dai *dai_props;
44
	struct snd_soc_dai_link *dai_link;
45
	u32 convert_rate;
46
	u32 convert_channels;
47 48
};

49 50 51
#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
52

53 54
#define DAI	"sound-dai"
#define CELL	"#sound-dai-cells"
55
#define PREFIX	"simple-audio-card,"
56

57
static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
58 59
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
60
	struct asoc_simple_card_priv *priv =	snd_soc_card_get_drvdata(rtd->card);
61
	struct asoc_simple_dai *dai_props =
62
		simple_priv_to_props(priv, rtd->num);
63

64
	return clk_prepare_enable(dai_props->clk);
65 66
}

67
static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
68 69
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
70
	struct asoc_simple_card_priv *priv =	snd_soc_card_get_drvdata(rtd->card);
71
	struct asoc_simple_dai *dai_props =
72
		simple_priv_to_props(priv, rtd->num);
73

74
	clk_disable_unprepare(dai_props->clk);
75 76
}

77 78 79
static struct snd_soc_ops asoc_simple_card_ops = {
	.startup = asoc_simple_card_startup,
	.shutdown = asoc_simple_card_shutdown,
80 81
};

82
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
83
{
84
	struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
85 86
	struct snd_soc_dai *dai;
	struct snd_soc_dai_link *dai_link;
87
	struct asoc_simple_dai *dai_props;
88
	int num = rtd->num;
89

90 91
	dai_link	= simple_priv_to_link(priv, num);
	dai_props	= simple_priv_to_props(priv, num);
92 93 94 95
	dai		= dai_link->dynamic ?
				rtd->cpu_dai :
				rtd->codec_dai;

96
	return asoc_simple_card_init_dai(dai, dai_props);
97 98
}

99
static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
100 101
					struct snd_pcm_hw_params *params)
{
102
	struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
103 104
	struct snd_interval *rate = hw_param_interval(params,
						      SNDRV_PCM_HW_PARAM_RATE);
105 106
	struct snd_interval *channels = hw_param_interval(params,
						SNDRV_PCM_HW_PARAM_CHANNELS);
107

108 109 110
	if (priv->convert_rate)
		rate->min =
		rate->max = priv->convert_rate;
111

112 113 114
	if (priv->convert_channels)
		channels->min =
		channels->max = priv->convert_channels;
115 116 117 118

	return 0;
}

119 120 121
static int asoc_simple_card_parse_links(struct device_node *np,
					struct asoc_simple_card_priv *priv,
					int idx, bool is_fe)
122
{
123 124 125
	struct device *dev = simple_priv_to_dev(priv);
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
	struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
126 127
	int ret;

128 129 130 131 132 133 134 135 136
	/* Parse TDM slot */
	ret = snd_soc_of_parse_tdm_slot(np,
					&dai_props->tx_slot_mask,
					&dai_props->rx_slot_mask,
					&dai_props->slots,
					&dai_props->slot_width);
	if (ret)
		return ret;

137
	if (is_fe) {
138 139
		int is_single_links = 0;

140 141 142 143 144 145 146 147
		/* BE is dummy */
		dai_link->codec_of_node		= NULL;
		dai_link->codec_dai_name	= "snd-soc-dummy-dai";
		dai_link->codec_name		= "snd-soc-dummy";

		/* FE settings */
		dai_link->dynamic		= 1;
		dai_link->dpcm_merged_format	= 1;
148 149 150 151

		ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
						 &is_single_links);
		if (ret)
152
			return ret;
153

154 155 156 157
		ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props);
		if (ret < 0)
			return ret;

158 159 160 161 162
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"fe.%s",
							dai_link->cpu_dai_name);
		if (ret < 0)
			return ret;
163

164
		asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
165 166 167 168 169
	} else {
		/* FE is dummy */
		dai_link->cpu_of_node		= NULL;
		dai_link->cpu_dai_name		= "snd-soc-dummy-dai";
		dai_link->cpu_name		= "snd-soc-dummy";
170

171 172
		/* BE settings */
		dai_link->no_pcm		= 1;
173
		dai_link->be_hw_params_fixup	= asoc_simple_card_be_hw_params_fixup;
174 175

		ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
176 177
		if (ret < 0)
			return ret;
178

179 180 181 182
		ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props);
		if (ret < 0)
			return ret;

183 184 185 186 187 188
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"be.%s",
							dai_link->codec_dai_name);
		if (ret < 0)
			return ret;

189 190 191 192
		snd_soc_of_parse_audio_prefix(&priv->snd_card,
					      &priv->codec_conf,
					      dai_link->codec_of_node,
					      PREFIX "prefix");
193 194
	}

195 196 197 198
	ret = asoc_simple_card_canonicalize_dailink(dai_link);
	if (ret < 0)
		return ret;

199 200
	dai_link->dpcm_playback		= 1;
	dai_link->dpcm_capture		= 1;
201 202
	dai_link->ops			= &asoc_simple_card_ops;
	dai_link->init			= asoc_simple_card_dai_init;
203 204

	dev_dbg(dev, "\t%s / %04x / %d\n",
205
		dai_link->name,
206
		dai_link->dai_fmt,
207
		dai_props->sysclk);
208

209
	return 0;
210 211
}

212 213
static int asoc_simple_card_dai_link_of(struct device_node *node,
				 struct asoc_simple_card_priv *priv)
214
{
215
	struct device *dev = simple_priv_to_dev(priv);
216 217 218 219 220 221 222 223 224
	struct snd_soc_dai_link *dai_link;
	struct device_node *np;
	unsigned int daifmt = 0;
	int ret, i;
	bool is_fe;

	/* find 1st codec */
	i = 0;
	for_each_child_of_node(node, np) {
225
		dai_link = simple_priv_to_link(priv, i);
226

227
		if (strcmp(np->name, PREFIX "codec") == 0) {
228
			ret = asoc_simple_card_parse_daifmt(dev, node, np,
229
							    PREFIX, &daifmt);
230 231 232 233 234 235 236 237 238
			if (ret < 0)
				return ret;
			break;
		}
		i++;
	}

	i = 0;
	for_each_child_of_node(node, np) {
239
		dai_link = simple_priv_to_link(priv, i);
240 241 242
		dai_link->dai_fmt = daifmt;

		is_fe = false;
243
		if (strcmp(np->name, PREFIX "cpu") == 0)
244 245
			is_fe = true;

246
		ret = asoc_simple_card_parse_links(np, priv, i, is_fe);
247 248 249 250 251 252 253 254
		if (ret < 0)
			return ret;
		i++;
	}

	return 0;
}

255 256
static int asoc_simple_card_parse_of(struct device_node *node,
			      struct asoc_simple_card_priv *priv,
257
			      struct device *dev)
258
{
259
	struct asoc_simple_dai *props;
260
	struct snd_soc_dai_link *links;
261
	int ret;
262
	int num;
263 264 265 266

	if (!node)
		return -EINVAL;

267 268 269 270 271 272 273 274 275
	num = of_get_child_count(node);
	props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL);
	links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL);
	if (!props || !links)
		return -ENOMEM;

	priv->dai_props	= props;
	priv->dai_link	= links;

276 277 278 279
	/* Init snd_soc_card */
	priv->snd_card.owner			= THIS_MODULE;
	priv->snd_card.dev			= dev;
	priv->snd_card.dai_link			= priv->dai_link;
280
	priv->snd_card.num_links		= num;
281 282
	priv->snd_card.codec_conf		= &priv->codec_conf;
	priv->snd_card.num_configs		= 1;
283

284 285 286
	ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing");
	if (ret < 0)
		return ret;
287

288
	/* sampling rate convert */
289
	of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate);
290

291
	/* channels transfer */
292
	of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels);
293

294
	ret = asoc_simple_card_dai_link_of(node, priv);
295 296
	if (ret < 0)
		return ret;
297

298
	ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
299 300
	if (ret < 0)
		return ret;
301

302 303 304 305 306
	dev_dbg(dev, "New card: %s\n",
		priv->snd_card.name ? priv->snd_card.name : "");
	dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
	dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);

307 308 309
	return 0;
}

310
static int asoc_simple_card_probe(struct platform_device *pdev)
311
{
312
	struct asoc_simple_card_priv *priv;
313 314 315 316 317 318 319 320 321
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	int ret;

	/* Allocate the private data */
	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

322
	ret = asoc_simple_card_parse_of(np, priv, dev);
323 324 325 326 327 328 329 330 331 332 333 334
	if (ret < 0) {
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "parse error %d\n", ret);
		goto err;
	}

	snd_soc_card_set_drvdata(&priv->snd_card, priv);

	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
	if (ret >= 0)
		return ret;
err:
335
	asoc_simple_card_clean_reference(&priv->snd_card);
336 337 338 339

	return ret;
}

340
static int asoc_simple_card_remove(struct platform_device *pdev)
341 342 343
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);

344
	return asoc_simple_card_clean_reference(card);
345 346
}

347
static struct platform_driver asoc_simple_card = {
348
	.driver = {
349
		.name = "simple-scu-audio-card",
350
		.of_match_table = asoc_simple_card_of_match,
351
	},
352 353
	.probe = asoc_simple_card_probe,
	.remove = asoc_simple_card_remove,
354 355
};

356
module_platform_driver(asoc_simple_card);
357

358
MODULE_ALIAS("platform:asoc-simple-scu-card");
359
MODULE_LICENSE("GPL");
360
MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
361
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");