cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

twl4030-audio.c (6840B)


      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
     25static struct platform_device *twl4030_audio_dev;
     26
     27struct twl4030_audio_resource {
     28	int request_count;
     29	u8 reg;
     30	u8 mask;
     31};
     32
     33struct 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 */
     44static 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
     63static 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 */
     78int 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}
    101EXPORT_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 */
    107int 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}
    137EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource);
    138
    139unsigned 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}
    145EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk);
    146
    147static 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
    164static 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
    178static 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
    261static 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
    269static const struct of_device_id twl4030_audio_of_match[] = {
    270	{.compatible = "ti,twl4030-audio", },
    271	{ },
    272};
    273MODULE_DEVICE_TABLE(of, twl4030_audio_of_match);
    274
    275static 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
    284module_platform_driver(twl4030_audio_driver);
    285
    286MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
    287MODULE_DESCRIPTION("TWL4030 audio block MFD driver");
    288MODULE_LICENSE("GPL");
    289MODULE_ALIAS("platform:twl4030-audio");