1 /** 2 * Freescale ALSA SoC Machine driver utility 3 * 4 * Author: Timur Tabi <timur@freescale.com> 5 * 6 * Copyright 2010 Freescale Semiconductor, Inc. 7 * 8 * This file is licensed under the terms of the GNU General Public License 9 * version 2. This program is licensed "as is" without any warranty of any 10 * kind, whether express or implied. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/of_address.h> 15 #include <linux/of_i2c.h> 16 #include <sound/soc.h> 17 18 #include "fsl_utils.h" 19 20 /** 21 * fsl_asoc_get_codec_dev_name - determine the dev_name for a codec node 22 * 23 * @np: pointer to the I2C device tree node 24 * @buf: buffer to be filled with the dev_name of the I2C device 25 * @len: the length of the buffer 26 * 27 * This function determines the dev_name for an I2C node. This is the name 28 * that would be returned by dev_name() if this device_node were part of a 29 * 'struct device' It's ugly and hackish, but it works. 30 * 31 * The dev_name for such devices include the bus number and I2C address. For 32 * example, "cs4270.0-004f". 33 */ 34 int fsl_asoc_get_codec_dev_name(struct device_node *np, char *buf, size_t len) 35 { 36 const u32 *iprop; 37 u32 addr; 38 char temp[DAI_NAME_SIZE]; 39 struct i2c_client *i2c; 40 41 of_modalias_node(np, temp, DAI_NAME_SIZE); 42 43 iprop = of_get_property(np, "reg", NULL); 44 if (!iprop) 45 return -EINVAL; 46 47 addr = be32_to_cpup(iprop); 48 49 /* We need the adapter number */ 50 i2c = of_find_i2c_device_by_node(np); 51 if (!i2c) { 52 put_device(&i2c->dev); 53 return -ENODEV; 54 } 55 56 snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr); 57 put_device(&i2c->dev); 58 59 return 0; 60 } 61 EXPORT_SYMBOL(fsl_asoc_get_codec_dev_name); 62 63 /** 64 * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node 65 * 66 * @ssi_np: pointer to the SSI device tree node 67 * @name: name of the phandle pointing to the dma channel 68 * @dai: ASoC DAI link pointer to be filled with platform_name 69 * @dma_channel_id: dma channel id to be returned 70 * @dma_id: dma id to be returned 71 * 72 * This function determines the dma and channel id for given SSI node. It 73 * also discovers the platform_name for the ASoC DAI link. 74 */ 75 int fsl_asoc_get_dma_channel(struct device_node *ssi_np, 76 const char *name, 77 struct snd_soc_dai_link *dai, 78 unsigned int *dma_channel_id, 79 unsigned int *dma_id) 80 { 81 struct resource res; 82 struct device_node *dma_channel_np, *dma_np; 83 const u32 *iprop; 84 int ret; 85 86 dma_channel_np = of_parse_phandle(ssi_np, name, 0); 87 if (!dma_channel_np) 88 return -EINVAL; 89 90 if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) { 91 of_node_put(dma_channel_np); 92 return -EINVAL; 93 } 94 95 /* Determine the dev_name for the device_node. This code mimics the 96 * behavior of of_device_make_bus_id(). We need this because ASoC uses 97 * the dev_name() of the device to match the platform (DMA) device with 98 * the CPU (SSI) device. It's all ugly and hackish, but it works (for 99 * now). 100 * 101 * dai->platform name should already point to an allocated buffer. 102 */ 103 ret = of_address_to_resource(dma_channel_np, 0, &res); 104 if (ret) { 105 of_node_put(dma_channel_np); 106 return ret; 107 } 108 snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", 109 (unsigned long long) res.start, dma_channel_np->name); 110 111 iprop = of_get_property(dma_channel_np, "cell-index", NULL); 112 if (!iprop) { 113 of_node_put(dma_channel_np); 114 return -EINVAL; 115 } 116 *dma_channel_id = be32_to_cpup(iprop); 117 118 dma_np = of_get_parent(dma_channel_np); 119 iprop = of_get_property(dma_np, "cell-index", NULL); 120 if (!iprop) { 121 of_node_put(dma_np); 122 return -EINVAL; 123 } 124 *dma_id = be32_to_cpup(iprop); 125 126 of_node_put(dma_np); 127 of_node_put(dma_channel_np); 128 129 return 0; 130 } 131 EXPORT_SYMBOL(fsl_asoc_get_dma_channel); 132 133 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 134 MODULE_DESCRIPTION("Freescale ASoC utility code"); 135 MODULE_LICENSE("GPL v2"); 136