blob: 46d91b4df21f0e380cbc2c9e9f634cb126462065 [file] [log] [blame]
/*
* Core MFD support for Cirrus Logic CS35L41 codec
*
* Copyright 2017 Cirrus Logic
*
* Author: David Rhodes <david.rhodes@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <sound/cs35l41.h>
#include <linux/mfd/cs35l41/core.h>
#define CS35L41_MFD_SYSFS_CLASS_NAME "cirrus"
static const struct mfd_cell cs35l41_devs[] = {
{ .name = "cs35l41-codec", },
{ .name = "cs35l41-cal", },
{ .name = "cs35l41-bd", },
{ .name = "cs35l41-pwr", },
};
static const char * const cs35l41_supplies[] = {
"VA",
"VP",
};
int cs35l41_dev_init(struct cs35l41_data *cs35l41)
{
int ret, i;
dev_set_drvdata(cs35l41->dev, cs35l41);
dev_info(cs35l41->dev, "Prince MFD core probe\n");
if (dev_get_platdata(cs35l41->dev))
memcpy(&cs35l41->pdata, dev_get_platdata(cs35l41->dev),
sizeof(cs35l41->pdata));
for (i = 0; i < ARRAY_SIZE(cs35l41_supplies); i++)
cs35l41->supplies[i].supply = cs35l41_supplies[i];
cs35l41->num_supplies = ARRAY_SIZE(cs35l41_supplies);
ret = devm_regulator_bulk_get(cs35l41->dev, cs35l41->num_supplies,
cs35l41->supplies);
if (ret != 0) {
dev_err(cs35l41->dev,
"Failed to request core supplies: %d\n",
ret);
return ret;
}
ret = regulator_bulk_enable(cs35l41->num_supplies, cs35l41->supplies);
if (ret != 0) {
dev_err(cs35l41->dev,
"Failed to enable core supplies: %d\n", ret);
return ret;
}
/* returning NULL can be an option if in stereo mode */
cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(cs35l41->reset_gpio)) {
ret = PTR_ERR(cs35l41->reset_gpio);
cs35l41->reset_gpio = NULL;
if (ret == -EBUSY) {
dev_info(cs35l41->dev,
"Reset line busy, assuming shared reset\n");
} else {
dev_err(cs35l41->dev,
"Failed to get reset GPIO: %d\n", ret);
goto err;
}
}
if (cs35l41->reset_gpio) {
usleep_range(1000, 1100);
gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
}
usleep_range(2000, 2100);
ret = mfd_add_devices(cs35l41->dev, PLATFORM_DEVID_AUTO, cs35l41_devs,
ARRAY_SIZE(cs35l41_devs),
NULL, 0, NULL);
if (ret) {
dev_err(cs35l41->dev, "Failed to add subdevices: %d\n", ret);
ret = -EINVAL;
goto err;
}
return 0;
err:
regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies);
return ret;
}
int cs35l41_dev_exit(struct cs35l41_data *cs35l41)
{
mfd_remove_devices(cs35l41->dev);
regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies);
return 0;
}