brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 2f185e9 Raw
263 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * MFD driver for wl1273 FM radio and audio codec submodules.4 *5 * Copyright (C) 2011 Nokia Corporation6 * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>7 */8 9#include <linux/mfd/wl1273-core.h>10#include <linux/slab.h>11#include <linux/module.h>12 13#define DRIVER_DESC "WL1273 FM Radio Core"14 15static const struct i2c_device_id wl1273_driver_id_table[] = {16	{ WL1273_FM_DRIVER_NAME },17	{ }18};19MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table);20 21static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value)22{23	struct i2c_client *client = core->client;24	u8 b[2];25	int r;26 27	r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b);28	if (r != 2) {29		dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg);30		return -EREMOTEIO;31	}32 33	*value = (u16)b[0] << 8 | b[1];34 35	return 0;36}37 38static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param)39{40	struct i2c_client *client = core->client;41	u8 buf[] = { (param >> 8) & 0xff, param & 0xff };42	int r;43 44	r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf);45	if (r) {46		dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd);47		return r;48	}49 50	return 0;51}52 53static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len)54{55	struct i2c_client *client = core->client;56	struct i2c_msg msg;57	int r;58 59	msg.addr = client->addr;60	msg.flags = 0;61	msg.buf = data;62	msg.len = len;63 64	r = i2c_transfer(client->adapter, &msg, 1);65	if (r != 1) {66		dev_err(&client->dev, "%s: write error.\n", __func__);67		return -EREMOTEIO;68	}69 70	return 0;71}72 73/**74 * wl1273_fm_set_audio() -	Set audio mode.75 * @core:			A pointer to the device struct.76 * @new_mode:			The new audio mode.77 *78 * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG.79 */80static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode)81{82	int r = 0;83 84	if (core->mode == WL1273_MODE_OFF ||85	    core->mode == WL1273_MODE_SUSPENDED)86		return -EPERM;87 88	if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) {89		r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,90					WL1273_PCM_DEF_MODE);91		if (r)92			goto out;93 94		r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,95					core->i2s_mode);96		if (r)97			goto out;98 99		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,100					WL1273_AUDIO_ENABLE_I2S);101		if (r)102			goto out;103 104	} else if (core->mode == WL1273_MODE_RX &&105		   new_mode == WL1273_AUDIO_ANALOG) {106		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,107					WL1273_AUDIO_ENABLE_ANALOG);108		if (r)109			goto out;110 111	} else if (core->mode == WL1273_MODE_TX &&112		   new_mode == WL1273_AUDIO_DIGITAL) {113		r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,114					core->i2s_mode);115		if (r)116			goto out;117 118		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,119					WL1273_AUDIO_IO_SET_I2S);120		if (r)121			goto out;122 123	} else if (core->mode == WL1273_MODE_TX &&124		   new_mode == WL1273_AUDIO_ANALOG) {125		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,126					WL1273_AUDIO_IO_SET_ANALOG);127		if (r)128			goto out;129	}130 131	core->audio_mode = new_mode;132out:133	return r;134}135 136/**137 * wl1273_fm_set_volume() -	Set volume.138 * @core:			A pointer to the device struct.139 * @volume:			The new volume value.140 */141static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)142{143	int r;144 145	if (volume > WL1273_MAX_VOLUME)146		return -EINVAL;147 148	if (core->volume == volume)149		return 0;150 151	r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume);152	if (r)153		return r;154 155	core->volume = volume;156	return 0;157}158 159static int wl1273_core_probe(struct i2c_client *client)160{161	struct wl1273_fm_platform_data *pdata = dev_get_platdata(&client->dev);162	struct wl1273_core *core;163	struct mfd_cell *cell;164	int children = 0;165	int r = 0;166 167	dev_dbg(&client->dev, "%s\n", __func__);168 169	if (!pdata) {170		dev_err(&client->dev, "No platform data.\n");171		return -EINVAL;172	}173 174	if (!(pdata->children & WL1273_RADIO_CHILD)) {175		dev_err(&client->dev, "Cannot function without radio child.\n");176		return -EINVAL;177	}178 179	core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);180	if (!core)181		return -ENOMEM;182 183	core->pdata = pdata;184	core->client = client;185	mutex_init(&core->lock);186 187	i2c_set_clientdata(client, core);188 189	dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__);190 191	cell = &core->cells[children];192	cell->name = "wl1273_fm_radio";193	cell->platform_data = &core;194	cell->pdata_size = sizeof(core);195	children++;196 197	core->read = wl1273_fm_read_reg;198	core->write = wl1273_fm_write_cmd;199	core->write_data = wl1273_fm_write_data;200	core->set_audio = wl1273_fm_set_audio;201	core->set_volume = wl1273_fm_set_volume;202 203	if (pdata->children & WL1273_CODEC_CHILD) {204		cell = &core->cells[children];205 206		dev_dbg(&client->dev, "%s: Have codec.\n", __func__);207		cell->name = "wl1273-codec";208		cell->platform_data = &core;209		cell->pdata_size = sizeof(core);210		children++;211	}212 213	dev_dbg(&client->dev, "%s: number of children: %d.\n",214		__func__, children);215 216	r = devm_mfd_add_devices(&client->dev, -1, core->cells,217				 children, NULL, 0, NULL);218	if (r)219		goto err;220 221	return 0;222 223err:224	pdata->free_resources();225 226	dev_dbg(&client->dev, "%s\n", __func__);227 228	return r;229}230 231static struct i2c_driver wl1273_core_driver = {232	.driver = {233		.name = WL1273_FM_DRIVER_NAME,234	},235	.probe = wl1273_core_probe,236	.id_table = wl1273_driver_id_table,237};238 239static int __init wl1273_core_init(void)240{241	int r;242 243	r = i2c_add_driver(&wl1273_core_driver);244	if (r) {245		pr_err(WL1273_FM_DRIVER_NAME246		       ": driver registration failed\n");247		return r;248	}249 250	return r;251}252 253static void __exit wl1273_core_exit(void)254{255	i2c_del_driver(&wl1273_core_driver);256}257late_initcall(wl1273_core_init);258module_exit(wl1273_core_exit);259 260MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");261MODULE_DESCRIPTION(DRIVER_DESC);262MODULE_LICENSE("GPL");263