root/drivers/mfd/twl4030-audio.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. twl4030_audio_set_resource
  2. twl4030_audio_get_resource
  3. twl4030_audio_enable_resource
  4. twl4030_audio_disable_resource
  5. twl4030_audio_get_mclk
  6. twl4030_audio_has_codec
  7. twl4030_audio_has_vibra
  8. twl4030_audio_probe
  9. twl4030_audio_remove

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

/* [<][>][^][v][top][bottom][index][help] */