Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
p1022_ds.c
Go to the documentation of this file.
1 
13 #include <linux/module.h>
14 #include <linux/interrupt.h>
15 #include <linux/of_device.h>
16 #include <linux/slab.h>
17 #include <sound/soc.h>
18 #include <asm/fsl_guts.h>
19 
20 #include "fsl_dma.h"
21 #include "fsl_ssi.h"
22 #include "fsl_utils.h"
23 
24 /* P1022-specific PMUXCR and DMUXCR bit definitions */
25 
26 #define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK 0x0001c000
27 #define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI 0x00010000
28 #define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI 0x00018000
29 
30 #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK 0x00000c00
31 #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI 0x00000000
32 
33 #define CCSR_GUTS_DMUXCR_PAD 1 /* DMA controller/channel set to pad */
34 #define CCSR_GUTS_DMUXCR_SSI 2 /* DMA controller/channel set to SSI */
35 
36 /*
37  * Set the DMACR register in the GUTS
38  *
39  * The DMACR register determines the source of initiated transfers for each
40  * channel on each DMA controller. Rather than have a bunch of repetitive
41  * macros for the bit patterns, we just have a function that calculates
42  * them.
43  *
44  * guts: Pointer to GUTS structure
45  * co: The DMA controller (0 or 1)
46  * ch: The channel on the DMA controller (0, 1, 2, or 3)
47  * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx)
48  */
49 static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
50  unsigned int co, unsigned int ch, unsigned int device)
51 {
52  unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch));
53 
54  clrsetbits_be32(&guts->dmuxcr, 3 << shift, device << shift);
55 }
56 
57 /* There's only one global utilities register */
58 static phys_addr_t guts_phys;
59 
66 struct machine_data {
67  struct snd_soc_dai_link dai[2];
69  unsigned int dai_format;
70  unsigned int codec_clk_direction;
71  unsigned int cpu_clk_direction;
72  unsigned int clk_frequency;
73  unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
74  unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
75  unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
76  char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
77 };
78 
86 static int p1022_ds_machine_probe(struct snd_soc_card *card)
87 {
88  struct machine_data *mdata =
89  container_of(card, struct machine_data, card);
90  struct ccsr_guts __iomem *guts;
91 
92  guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
93  if (!guts) {
94  dev_err(card->dev, "could not map global utilities\n");
95  return -ENOMEM;
96  }
97 
98  /* Enable SSI Tx signal */
99  clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK,
101 
102  /* Enable SSI Rx signal */
103  clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK,
105 
106  /* Enable DMA Channel for SSI */
107  guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0],
109 
110  guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1],
112 
113  iounmap(guts);
114 
115  return 0;
116 }
117 
125 static int p1022_ds_startup(struct snd_pcm_substream *substream)
126 {
127  struct snd_soc_pcm_runtime *rtd = substream->private_data;
128  struct machine_data *mdata =
129  container_of(rtd->card, struct machine_data, card);
130  struct device *dev = rtd->card->dev;
131  int ret = 0;
132 
133  /* Tell the codec driver what the serial protocol is. */
134  ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format);
135  if (ret < 0) {
136  dev_err(dev, "could not set codec driver audio format\n");
137  return ret;
138  }
139 
140  /*
141  * Tell the codec driver what the MCLK frequency is, and whether it's
142  * a slave or master.
143  */
144  ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, mdata->clk_frequency,
145  mdata->codec_clk_direction);
146  if (ret < 0) {
147  dev_err(dev, "could not set codec driver clock params\n");
148  return ret;
149  }
150 
151  return 0;
152 }
153 
160 static int p1022_ds_machine_remove(struct snd_soc_card *card)
161 {
162  struct machine_data *mdata =
163  container_of(card, struct machine_data, card);
164  struct ccsr_guts __iomem *guts;
165 
166  guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
167  if (!guts) {
168  dev_err(card->dev, "could not map global utilities\n");
169  return -ENOMEM;
170  }
171 
172  /* Restore the signal routing */
173  clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK);
174  clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK);
175  guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], 0);
176  guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], 0);
177 
178  iounmap(guts);
179 
180  return 0;
181 }
182 
186 static struct snd_soc_ops p1022_ds_ops = {
187  .startup = p1022_ds_startup,
188 };
189 
197 static int p1022_ds_probe(struct platform_device *pdev)
198 {
199  struct device *dev = pdev->dev.parent;
200  /* ssi_pdev is the platform device for the SSI node that probed us */
201  struct platform_device *ssi_pdev =
202  container_of(dev, struct platform_device, dev);
203  struct device_node *np = ssi_pdev->dev.of_node;
204  struct device_node *codec_np = NULL;
205  struct machine_data *mdata;
206  int ret = -ENODEV;
207  const char *sprop;
208  const u32 *iprop;
209 
210  /* Find the codec node for this SSI. */
211  codec_np = of_parse_phandle(np, "codec-handle", 0);
212  if (!codec_np) {
213  dev_err(dev, "could not find codec node\n");
214  return -EINVAL;
215  }
216 
217  mdata = kzalloc(sizeof(struct machine_data), GFP_KERNEL);
218  if (!mdata) {
219  ret = -ENOMEM;
220  goto error_put;
221  }
222 
223  mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
224  mdata->dai[0].ops = &p1022_ds_ops;
225 
226  /* ASoC core can match codec with device node */
227  mdata->dai[0].codec_of_node = codec_np;
228 
229  /* We register two DAIs per SSI, one for playback and the other for
230  * capture. We support codecs that have separate DAIs for both playback
231  * and capture.
232  */
233  memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link));
234 
235  /* The DAI names from the codec (snd_soc_dai_driver.name) */
236  mdata->dai[0].codec_dai_name = "wm8776-hifi-playback";
237  mdata->dai[1].codec_dai_name = "wm8776-hifi-capture";
238 
239  /* Get the device ID */
240  iprop = of_get_property(np, "cell-index", NULL);
241  if (!iprop) {
242  dev_err(&pdev->dev, "cell-index property not found\n");
243  ret = -EINVAL;
244  goto error;
245  }
246  mdata->ssi_id = be32_to_cpup(iprop);
247 
248  /* Get the serial format and clock direction. */
249  sprop = of_get_property(np, "fsl,mode", NULL);
250  if (!sprop) {
251  dev_err(&pdev->dev, "fsl,mode property not found\n");
252  ret = -EINVAL;
253  goto error;
254  }
255 
256  if (strcasecmp(sprop, "i2s-slave") == 0) {
261 
262  /* In i2s-slave mode, the codec has its own clock source, so we
263  * need to get the frequency from the device tree and pass it to
264  * the codec driver.
265  */
266  iprop = of_get_property(codec_np, "clock-frequency", NULL);
267  if (!iprop || !*iprop) {
268  dev_err(&pdev->dev, "codec bus-frequency "
269  "property is missing or invalid\n");
270  ret = -EINVAL;
271  goto error;
272  }
273  mdata->clk_frequency = be32_to_cpup(iprop);
274  } else if (strcasecmp(sprop, "i2s-master") == 0) {
279  } else if (strcasecmp(sprop, "lj-slave") == 0) {
284  } else if (strcasecmp(sprop, "lj-master") == 0) {
289  } else if (strcasecmp(sprop, "rj-slave") == 0) {
294  } else if (strcasecmp(sprop, "rj-master") == 0) {
299  } else if (strcasecmp(sprop, "ac97-slave") == 0) {
304  } else if (strcasecmp(sprop, "ac97-master") == 0) {
309  } else {
310  dev_err(&pdev->dev,
311  "unrecognized fsl,mode property '%s'\n", sprop);
312  ret = -EINVAL;
313  goto error;
314  }
315 
316  if (!mdata->clk_frequency) {
317  dev_err(&pdev->dev, "unknown clock frequency\n");
318  ret = -EINVAL;
319  goto error;
320  }
321 
322  /* Find the playback DMA channel to use. */
323  mdata->dai[0].platform_name = mdata->platform_name[0];
324  ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
325  &mdata->dma_channel_id[0],
326  &mdata->dma_id[0]);
327  if (ret) {
328  dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
329  goto error;
330  }
331 
332  /* Find the capture DMA channel to use. */
333  mdata->dai[1].platform_name = mdata->platform_name[1];
334  ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
335  &mdata->dma_channel_id[1],
336  &mdata->dma_id[1]);
337  if (ret) {
338  dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
339  goto error;
340  }
341 
342  /* Initialize our DAI data structure. */
343  mdata->dai[0].stream_name = "playback";
344  mdata->dai[1].stream_name = "capture";
345  mdata->dai[0].name = mdata->dai[0].stream_name;
346  mdata->dai[1].name = mdata->dai[1].stream_name;
347 
348  mdata->card.probe = p1022_ds_machine_probe;
349  mdata->card.remove = p1022_ds_machine_remove;
350  mdata->card.name = pdev->name; /* The platform driver name */
351  mdata->card.owner = THIS_MODULE;
352  mdata->card.dev = &pdev->dev;
353  mdata->card.num_links = 2;
354  mdata->card.dai_link = mdata->dai;
355 
356  /* Register with ASoC */
357  ret = snd_soc_register_card(&mdata->card);
358  if (ret) {
359  dev_err(&pdev->dev, "could not register card\n");
360  goto error;
361  }
362 
363  of_node_put(codec_np);
364 
365  return 0;
366 
367 error:
368  kfree(mdata);
369 error_put:
370  of_node_put(codec_np);
371  return ret;
372 }
373 
379 static int __devexit p1022_ds_remove(struct platform_device *pdev)
380 {
381  struct snd_soc_card *card = platform_get_drvdata(pdev);
382  struct machine_data *mdata =
383  container_of(card, struct machine_data, card);
384 
386  kfree(mdata);
387 
388  return 0;
389 }
390 
391 static struct platform_driver p1022_ds_driver = {
392  .probe = p1022_ds_probe,
393  .remove = __devexit_p(p1022_ds_remove),
394  .driver = {
395  /*
396  * The name must match 'compatible' property in the device tree,
397  * in lowercase letters.
398  */
399  .name = "snd-soc-p1022ds",
400  .owner = THIS_MODULE,
401  },
402 };
403 
409 static int __init p1022_ds_init(void)
410 {
411  struct device_node *guts_np;
412  struct resource res;
413 
414  /* Get the physical address of the global utilities registers */
415  guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts");
416  if (of_address_to_resource(guts_np, 0, &res)) {
417  pr_err("snd-soc-p1022ds: missing/invalid global utils node\n");
418  of_node_put(guts_np);
419  return -EINVAL;
420  }
421  guts_phys = res.start;
422  of_node_put(guts_np);
423 
424  return platform_driver_register(&p1022_ds_driver);
425 }
426 
432 static void __exit p1022_ds_exit(void)
433 {
434  platform_driver_unregister(&p1022_ds_driver);
435 }
436 
437 module_init(p1022_ds_init);
438 module_exit(p1022_ds_exit);
439 
440 MODULE_AUTHOR("Timur Tabi <[email protected]>");
441 MODULE_DESCRIPTION("Freescale P1022 DS ALSA SoC machine driver");
442 MODULE_LICENSE("GPL v2");