1 /* 2 * MFD driver for twl4030 audio submodule, which contains an audio codec, and 3 * the vibra control. 4 * 5 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 6 * 7 * Copyright: (C) 2009 Nokia Corporation 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 21 * 02110-1301 USA 22 * 23 */ 24 25 #include <linux/module.h> 26 #include <linux/types.h> 27 #include <linux/slab.h> 28 #include <linux/kernel.h> 29 #include <linux/fs.h> 30 #include <linux/platform_device.h> 31 #include <linux/of.h> 32 #include <linux/of_platform.h> 33 #include <linux/i2c/twl.h> 34 #include <linux/mfd/core.h> 35 #include <linux/mfd/twl4030-audio.h> 36 37 #define TWL4030_AUDIO_CELLS 2 38 39 static struct platform_device *twl4030_audio_dev; 40 41 struct twl4030_audio_resource { 42 int request_count; 43 u8 reg; 44 u8 mask; 45 }; 46 47 struct twl4030_audio { 48 unsigned int audio_mclk; 49 struct mutex mutex; 50 struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX]; 51 struct mfd_cell cells[TWL4030_AUDIO_CELLS]; 52 }; 53 54 /* 55 * Modify the resource, the function returns the content of the register 56 * after the modification. 57 */ 58 static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable) 59 { 60 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); 61 u8 val; 62 63 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, 64 audio->resource[id].reg); 65 66 if (enable) 67 val |= audio->resource[id].mask; 68 else 69 val &= ~audio->resource[id].mask; 70 71 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 72 val, audio->resource[id].reg); 73 74 return val; 75 } 76 77 static inline int twl4030_audio_get_resource(enum twl4030_audio_res id) 78 { 79 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); 80 u8 val; 81 82 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, 83 audio->resource[id].reg); 84 85 return val; 86 } 87 88 /* 89 * Enable the resource. 90 * The function returns with error or the content of the register 91 */ 92 int twl4030_audio_enable_resource(enum twl4030_audio_res id) 93 { 94 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); 95 int val; 96 97 if (id >= TWL4030_AUDIO_RES_MAX) { 98 dev_err(&twl4030_audio_dev->dev, 99 "Invalid resource ID (%u)\n", id); 100 return -EINVAL; 101 } 102 103 mutex_lock(&audio->mutex); 104 if (!audio->resource[id].request_count) 105 /* Resource was disabled, enable it */ 106 val = twl4030_audio_set_resource(id, 1); 107 else 108 val = twl4030_audio_get_resource(id); 109 110 audio->resource[id].request_count++; 111 mutex_unlock(&audio->mutex); 112 113 return val; 114 } 115 EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource); 116 117 /* 118 * Disable the resource. 119 * The function returns with error or the content of the register 120 */ 121 int twl4030_audio_disable_resource(enum twl4030_audio_res id) 122 { 123 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); 124 int val; 125 126 if (id >= TWL4030_AUDIO_RES_MAX) { 127 dev_err(&twl4030_audio_dev->dev, 128 "Invalid resource ID (%u)\n", id); 129 return -EINVAL; 130 } 131 132 mutex_lock(&audio->mutex); 133 if (!audio->resource[id].request_count) { 134 dev_err(&twl4030_audio_dev->dev, 135 "Resource has been disabled already (%u)\n", id); 136 mutex_unlock(&audio->mutex); 137 return -EPERM; 138 } 139 audio->resource[id].request_count--; 140 141 if (!audio->resource[id].request_count) 142 /* Resource can be disabled now */ 143 val = twl4030_audio_set_resource(id, 0); 144 else 145 val = twl4030_audio_get_resource(id); 146 147 mutex_unlock(&audio->mutex); 148 149 return val; 150 } 151 EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource); 152 153 unsigned int twl4030_audio_get_mclk(void) 154 { 155 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); 156 157 return audio->audio_mclk; 158 } 159 EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); 160 161 static bool twl4030_audio_has_codec(struct twl4030_audio_data *pdata, 162 struct device_node *node) 163 { 164 if (pdata && pdata->codec) 165 return true; 166 167 if (of_find_node_by_name(node, "codec")) 168 return true; 169 170 return false; 171 } 172 173 static bool twl4030_audio_has_vibra(struct twl4030_audio_data *pdata, 174 struct device_node *node) 175 { 176 int vibra; 177 178 if (pdata && pdata->vibra) 179 return true; 180 181 if (!of_property_read_u32(node, "ti,enable-vibra", &vibra) && vibra) 182 return true; 183 184 return false; 185 } 186 187 static int twl4030_audio_probe(struct platform_device *pdev) 188 { 189 struct twl4030_audio *audio; 190 struct twl4030_audio_data *pdata = dev_get_platdata(&pdev->dev); 191 struct device_node *node = pdev->dev.of_node; 192 struct mfd_cell *cell = NULL; 193 int ret, childs = 0; 194 u8 val; 195 196 if (!pdata && !node) { 197 dev_err(&pdev->dev, "Platform data is missing\n"); 198 return -EINVAL; 199 } 200 201 audio = devm_kzalloc(&pdev->dev, sizeof(struct twl4030_audio), 202 GFP_KERNEL); 203 if (!audio) 204 return -ENOMEM; 205 206 mutex_init(&audio->mutex); 207 audio->audio_mclk = twl_get_hfclk_rate(); 208 209 /* Configure APLL_INFREQ and disable APLL if enabled */ 210 switch (audio->audio_mclk) { 211 case 19200000: 212 val = TWL4030_APLL_INFREQ_19200KHZ; 213 break; 214 case 26000000: 215 val = TWL4030_APLL_INFREQ_26000KHZ; 216 break; 217 case 38400000: 218 val = TWL4030_APLL_INFREQ_38400KHZ; 219 break; 220 default: 221 dev_err(&pdev->dev, "Invalid audio_mclk\n"); 222 return -EINVAL; 223 } 224 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, val, TWL4030_REG_APLL_CTL); 225 226 /* Codec power */ 227 audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE; 228 audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ; 229 230 /* PLL */ 231 audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; 232 audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; 233 234 if (twl4030_audio_has_codec(pdata, node)) { 235 cell = &audio->cells[childs]; 236 cell->name = "twl4030-codec"; 237 if (pdata) { 238 cell->platform_data = pdata->codec; 239 cell->pdata_size = sizeof(*pdata->codec); 240 } 241 childs++; 242 } 243 if (twl4030_audio_has_vibra(pdata, node)) { 244 cell = &audio->cells[childs]; 245 cell->name = "twl4030-vibra"; 246 if (pdata) { 247 cell->platform_data = pdata->vibra; 248 cell->pdata_size = sizeof(*pdata->vibra); 249 } 250 childs++; 251 } 252 253 platform_set_drvdata(pdev, audio); 254 twl4030_audio_dev = pdev; 255 256 if (childs) 257 ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, 258 childs, NULL, 0, NULL); 259 else { 260 dev_err(&pdev->dev, "No platform data found for childs\n"); 261 ret = -ENODEV; 262 } 263 264 if (ret) 265 twl4030_audio_dev = NULL; 266 267 return ret; 268 } 269 270 static int twl4030_audio_remove(struct platform_device *pdev) 271 { 272 mfd_remove_devices(&pdev->dev); 273 twl4030_audio_dev = NULL; 274 275 return 0; 276 } 277 278 static const struct of_device_id twl4030_audio_of_match[] = { 279 {.compatible = "ti,twl4030-audio", }, 280 { }, 281 }; 282 MODULE_DEVICE_TABLE(of, twl4030_audio_of_match); 283 284 static struct platform_driver twl4030_audio_driver = { 285 .driver = { 286 .owner = THIS_MODULE, 287 .name = "twl4030-audio", 288 .of_match_table = twl4030_audio_of_match, 289 }, 290 .probe = twl4030_audio_probe, 291 .remove = twl4030_audio_remove, 292 }; 293 294 module_platform_driver(twl4030_audio_driver); 295 296 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 297 MODULE_DESCRIPTION("TWL4030 audio block MFD driver"); 298 MODULE_LICENSE("GPL"); 299 MODULE_ALIAS("platform:twl4030-audio"); 300