0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/module.h>
0012 #include <linux/types.h>
0013 #include <linux/slab.h>
0014 #include <linux/kernel.h>
0015 #include <linux/fs.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/of.h>
0018 #include <linux/of_platform.h>
0019 #include <linux/mfd/twl.h>
0020 #include <linux/mfd/core.h>
0021 #include <linux/mfd/twl4030-audio.h>
0022
0023 #define TWL4030_AUDIO_CELLS 2
0024
0025 static struct platform_device *twl4030_audio_dev;
0026
0027 struct twl4030_audio_resource {
0028 int request_count;
0029 u8 reg;
0030 u8 mask;
0031 };
0032
0033 struct twl4030_audio {
0034 unsigned int audio_mclk;
0035 struct mutex mutex;
0036 struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX];
0037 struct mfd_cell cells[TWL4030_AUDIO_CELLS];
0038 };
0039
0040
0041
0042
0043
0044 static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable)
0045 {
0046 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
0047 u8 val;
0048
0049 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
0050 audio->resource[id].reg);
0051
0052 if (enable)
0053 val |= audio->resource[id].mask;
0054 else
0055 val &= ~audio->resource[id].mask;
0056
0057 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
0058 val, audio->resource[id].reg);
0059
0060 return val;
0061 }
0062
0063 static inline int twl4030_audio_get_resource(enum twl4030_audio_res id)
0064 {
0065 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
0066 u8 val;
0067
0068 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
0069 audio->resource[id].reg);
0070
0071 return val;
0072 }
0073
0074
0075
0076
0077
0078 int twl4030_audio_enable_resource(enum twl4030_audio_res id)
0079 {
0080 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
0081 int val;
0082
0083 if (id >= TWL4030_AUDIO_RES_MAX) {
0084 dev_err(&twl4030_audio_dev->dev,
0085 "Invalid resource ID (%u)\n", id);
0086 return -EINVAL;
0087 }
0088
0089 mutex_lock(&audio->mutex);
0090 if (!audio->resource[id].request_count)
0091
0092 val = twl4030_audio_set_resource(id, 1);
0093 else
0094 val = twl4030_audio_get_resource(id);
0095
0096 audio->resource[id].request_count++;
0097 mutex_unlock(&audio->mutex);
0098
0099 return val;
0100 }
0101 EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
0102
0103
0104
0105
0106
0107 int twl4030_audio_disable_resource(enum twl4030_audio_res id)
0108 {
0109 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
0110 int val;
0111
0112 if (id >= TWL4030_AUDIO_RES_MAX) {
0113 dev_err(&twl4030_audio_dev->dev,
0114 "Invalid resource ID (%u)\n", id);
0115 return -EINVAL;
0116 }
0117
0118 mutex_lock(&audio->mutex);
0119 if (!audio->resource[id].request_count) {
0120 dev_err(&twl4030_audio_dev->dev,
0121 "Resource has been disabled already (%u)\n", id);
0122 mutex_unlock(&audio->mutex);
0123 return -EPERM;
0124 }
0125 audio->resource[id].request_count--;
0126
0127 if (!audio->resource[id].request_count)
0128
0129 val = twl4030_audio_set_resource(id, 0);
0130 else
0131 val = twl4030_audio_get_resource(id);
0132
0133 mutex_unlock(&audio->mutex);
0134
0135 return val;
0136 }
0137 EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource);
0138
0139 unsigned int twl4030_audio_get_mclk(void)
0140 {
0141 struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
0142
0143 return audio->audio_mclk;
0144 }
0145 EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk);
0146
0147 static bool twl4030_audio_has_codec(struct twl4030_audio_data *pdata,
0148 struct device_node *parent)
0149 {
0150 struct device_node *node;
0151
0152 if (pdata && pdata->codec)
0153 return true;
0154
0155 node = of_get_child_by_name(parent, "codec");
0156 if (node) {
0157 of_node_put(node);
0158 return true;
0159 }
0160
0161 return false;
0162 }
0163
0164 static bool twl4030_audio_has_vibra(struct twl4030_audio_data *pdata,
0165 struct device_node *node)
0166 {
0167 int vibra;
0168
0169 if (pdata && pdata->vibra)
0170 return true;
0171
0172 if (!of_property_read_u32(node, "ti,enable-vibra", &vibra) && vibra)
0173 return true;
0174
0175 return false;
0176 }
0177
0178 static int twl4030_audio_probe(struct platform_device *pdev)
0179 {
0180 struct twl4030_audio *audio;
0181 struct twl4030_audio_data *pdata = dev_get_platdata(&pdev->dev);
0182 struct device_node *node = pdev->dev.of_node;
0183 struct mfd_cell *cell = NULL;
0184 int ret, childs = 0;
0185 u8 val;
0186
0187 if (!pdata && !node) {
0188 dev_err(&pdev->dev, "Platform data is missing\n");
0189 return -EINVAL;
0190 }
0191
0192 audio = devm_kzalloc(&pdev->dev, sizeof(struct twl4030_audio),
0193 GFP_KERNEL);
0194 if (!audio)
0195 return -ENOMEM;
0196
0197 mutex_init(&audio->mutex);
0198 audio->audio_mclk = twl_get_hfclk_rate();
0199
0200
0201 switch (audio->audio_mclk) {
0202 case 19200000:
0203 val = TWL4030_APLL_INFREQ_19200KHZ;
0204 break;
0205 case 26000000:
0206 val = TWL4030_APLL_INFREQ_26000KHZ;
0207 break;
0208 case 38400000:
0209 val = TWL4030_APLL_INFREQ_38400KHZ;
0210 break;
0211 default:
0212 dev_err(&pdev->dev, "Invalid audio_mclk\n");
0213 return -EINVAL;
0214 }
0215 twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, val, TWL4030_REG_APLL_CTL);
0216
0217
0218 audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
0219 audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ;
0220
0221
0222 audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL;
0223 audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN;
0224
0225 if (twl4030_audio_has_codec(pdata, node)) {
0226 cell = &audio->cells[childs];
0227 cell->name = "twl4030-codec";
0228 if (pdata) {
0229 cell->platform_data = pdata->codec;
0230 cell->pdata_size = sizeof(*pdata->codec);
0231 }
0232 childs++;
0233 }
0234 if (twl4030_audio_has_vibra(pdata, node)) {
0235 cell = &audio->cells[childs];
0236 cell->name = "twl4030-vibra";
0237 if (pdata) {
0238 cell->platform_data = pdata->vibra;
0239 cell->pdata_size = sizeof(*pdata->vibra);
0240 }
0241 childs++;
0242 }
0243
0244 platform_set_drvdata(pdev, audio);
0245 twl4030_audio_dev = pdev;
0246
0247 if (childs)
0248 ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
0249 childs, NULL, 0, NULL);
0250 else {
0251 dev_err(&pdev->dev, "No platform data found for childs\n");
0252 ret = -ENODEV;
0253 }
0254
0255 if (ret)
0256 twl4030_audio_dev = NULL;
0257
0258 return ret;
0259 }
0260
0261 static int twl4030_audio_remove(struct platform_device *pdev)
0262 {
0263 mfd_remove_devices(&pdev->dev);
0264 twl4030_audio_dev = NULL;
0265
0266 return 0;
0267 }
0268
0269 static const struct of_device_id twl4030_audio_of_match[] = {
0270 {.compatible = "ti,twl4030-audio", },
0271 { },
0272 };
0273 MODULE_DEVICE_TABLE(of, twl4030_audio_of_match);
0274
0275 static struct platform_driver twl4030_audio_driver = {
0276 .driver = {
0277 .name = "twl4030-audio",
0278 .of_match_table = twl4030_audio_of_match,
0279 },
0280 .probe = twl4030_audio_probe,
0281 .remove = twl4030_audio_remove,
0282 };
0283
0284 module_platform_driver(twl4030_audio_driver);
0285
0286 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
0287 MODULE_DESCRIPTION("TWL4030 audio block MFD driver");
0288 MODULE_LICENSE("GPL");
0289 MODULE_ALIAS("platform:twl4030-audio");