]>
Commit | Line | Data |
---|---|---|
48cd6b5d WB |
1 | /* |
2 | * ASoC driver for PROTO AudioCODEC (with a WM8731) | |
3 | * connected to a Raspberry Pi | |
4 | * | |
5 | * Author: Florian Meier, <koalo@koalo.de> | |
6 | * Copyright 2013 | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/platform_device.h> | |
15 | ||
16 | #include <sound/core.h> | |
17 | #include <sound/pcm.h> | |
18 | #include <sound/soc.h> | |
19 | #include <sound/jack.h> | |
20 | ||
21 | #include "../codecs/wm8731.h" | |
22 | ||
23 | static const unsigned int wm8731_rates_12288000[] = { | |
24 | 8000, 32000, 48000, 96000, | |
25 | }; | |
26 | ||
27 | static struct snd_pcm_hw_constraint_list wm8731_constraints_12288000 = { | |
28 | .list = wm8731_rates_12288000, | |
29 | .count = ARRAY_SIZE(wm8731_rates_12288000), | |
30 | }; | |
31 | ||
32 | static int snd_rpi_proto_startup(struct snd_pcm_substream *substream) | |
33 | { | |
34 | /* Setup constraints, because there is a 12.288 MHz XTAL on the board */ | |
35 | snd_pcm_hw_constraint_list(substream->runtime, 0, | |
36 | SNDRV_PCM_HW_PARAM_RATE, | |
37 | &wm8731_constraints_12288000); | |
38 | return 0; | |
39 | } | |
40 | ||
41 | static int snd_rpi_proto_hw_params(struct snd_pcm_substream *substream, | |
42 | struct snd_pcm_hw_params *params) | |
43 | { | |
44 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
45 | struct snd_soc_codec *codec = rtd->codec; | |
46 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | |
47 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
48 | int sysclk = 12288000; /* This is fixed on this board */ | |
49 | ||
50 | /* Set proto bclk */ | |
51 | int ret = snd_soc_dai_set_bclk_ratio(cpu_dai,32*2); | |
52 | if (ret < 0){ | |
53 | dev_err(codec->dev, | |
54 | "Failed to set BCLK ratio %d\n", ret); | |
55 | return ret; | |
56 | } | |
57 | ||
58 | /* Set proto sysclk */ | |
59 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, | |
60 | sysclk, SND_SOC_CLOCK_IN); | |
61 | if (ret < 0) { | |
62 | dev_err(codec->dev, | |
63 | "Failed to set WM8731 SYSCLK: %d\n", ret); | |
64 | return ret; | |
65 | } | |
66 | ||
67 | return 0; | |
68 | } | |
69 | ||
70 | /* machine stream operations */ | |
71 | static struct snd_soc_ops snd_rpi_proto_ops = { | |
72 | .startup = snd_rpi_proto_startup, | |
73 | .hw_params = snd_rpi_proto_hw_params, | |
74 | }; | |
75 | ||
76 | static struct snd_soc_dai_link snd_rpi_proto_dai[] = { | |
77 | { | |
78 | .name = "WM8731", | |
79 | .stream_name = "WM8731 HiFi", | |
80 | .cpu_dai_name = "bcm2708-i2s.0", | |
81 | .codec_dai_name = "wm8731-hifi", | |
82 | .platform_name = "bcm2708-i2s.0", | |
83 | .codec_name = "wm8731.1-001a", | |
84 | .dai_fmt = SND_SOC_DAIFMT_I2S | |
85 | | SND_SOC_DAIFMT_NB_NF | |
86 | | SND_SOC_DAIFMT_CBM_CFM, | |
87 | .ops = &snd_rpi_proto_ops, | |
88 | }, | |
89 | }; | |
90 | ||
91 | /* audio machine driver */ | |
92 | static struct snd_soc_card snd_rpi_proto = { | |
93 | .name = "snd_rpi_proto", | |
94 | .owner = THIS_MODULE, | |
95 | .dai_link = snd_rpi_proto_dai, | |
96 | .num_links = ARRAY_SIZE(snd_rpi_proto_dai), | |
97 | }; | |
98 | ||
99 | static int snd_rpi_proto_probe(struct platform_device *pdev) | |
100 | { | |
101 | int ret = 0; | |
102 | ||
103 | snd_rpi_proto.dev = &pdev->dev; | |
104 | ||
105 | if (pdev->dev.of_node) { | |
106 | struct device_node *i2s_node; | |
107 | struct snd_soc_dai_link *dai = &snd_rpi_proto_dai[0]; | |
108 | i2s_node = of_parse_phandle(pdev->dev.of_node, | |
109 | "i2s-controller", 0); | |
110 | ||
111 | if (i2s_node) { | |
112 | dai->cpu_dai_name = NULL; | |
113 | dai->cpu_of_node = i2s_node; | |
114 | dai->platform_name = NULL; | |
115 | dai->platform_of_node = i2s_node; | |
116 | } | |
117 | } | |
118 | ||
119 | ret = snd_soc_register_card(&snd_rpi_proto); | |
49dbf1af | 120 | if (ret && ret != -EPROBE_DEFER) |
48cd6b5d WB |
121 | dev_err(&pdev->dev, |
122 | "snd_soc_register_card() failed: %d\n", ret); | |
48cd6b5d WB |
123 | |
124 | return ret; | |
125 | } | |
126 | ||
127 | ||
128 | static int snd_rpi_proto_remove(struct platform_device *pdev) | |
129 | { | |
130 | return snd_soc_unregister_card(&snd_rpi_proto); | |
131 | } | |
132 | ||
133 | static const struct of_device_id snd_rpi_proto_of_match[] = { | |
134 | { .compatible = "rpi,rpi-proto", }, | |
135 | {}, | |
136 | }; | |
137 | MODULE_DEVICE_TABLE(of, snd_rpi_proto_of_match); | |
138 | ||
139 | static struct platform_driver snd_rpi_proto_driver = { | |
140 | .driver = { | |
141 | .name = "snd-rpi-proto", | |
142 | .owner = THIS_MODULE, | |
143 | .of_match_table = snd_rpi_proto_of_match, | |
144 | }, | |
145 | .probe = snd_rpi_proto_probe, | |
146 | .remove = snd_rpi_proto_remove, | |
147 | }; | |
148 | ||
149 | module_platform_driver(snd_rpi_proto_driver); | |
150 | ||
151 | MODULE_AUTHOR("Florian Meier"); | |
152 | MODULE_DESCRIPTION("ASoC Driver for Raspberry Pi connected to PROTO board (WM8731)"); | |
153 | MODULE_LICENSE("GPL"); |