605b42f92c
Kernel audio drivers can be categorised into below folders. asoc - ALSA based drivers, asoc/codecs - codec drivers, ipc - APR IPC communication drivers, dsp - DSP low level drivers/Audio ION/ADSP Loader, dsp/codecs - Native encoders and decoders, soc - SoC based drivers(pinctrl/regmap/soundwire) Restructure drivers to above folder format. Include directories also follow above format. Change-Id: I8fa0857baaacd47db126fb5c1f1f5ed7e886dbc0 Signed-off-by: Laxminath Kasam <lkasam@codeaurora.org>
435 lines
11 KiB
C
435 lines
11 KiB
C
/*
|
|
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/gpio.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/pinctrl/pinconf-generic.h>
|
|
#include <linux/pinctrl/pinconf.h>
|
|
#include <linux/pinctrl/pinmux.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <asoc/wcd934x_registers.h>
|
|
|
|
#include "core.h"
|
|
#include "pinctrl-utils.h"
|
|
|
|
#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE
|
|
#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA
|
|
#define WCD_GPIO_PULL_UP 1
|
|
#define WCD_GPIO_PULL_DOWN 2
|
|
#define WCD_GPIO_BIAS_DISABLE 3
|
|
#define WCD_GPIO_STRING_LEN 20
|
|
|
|
/**
|
|
* struct wcd_gpio_pad - keep current GPIO settings
|
|
* @offset: offset of gpio.
|
|
* @is_valid: Set to false, when GPIO in high Z state.
|
|
* @value: value of a pin
|
|
* @output_enabled: Set to true if GPIO is output and false if it is input
|
|
* @pullup: Constant current which flow through GPIO output buffer.
|
|
* @strength: Drive strength of a pin
|
|
*/
|
|
struct wcd_gpio_pad {
|
|
u16 offset;
|
|
bool is_valid;
|
|
bool value;
|
|
bool output_enabled;
|
|
unsigned int pullup;
|
|
unsigned int strength;
|
|
};
|
|
|
|
struct wcd_gpio_priv {
|
|
struct device *dev;
|
|
struct regmap *map;
|
|
struct pinctrl_dev *ctrl;
|
|
struct gpio_chip chip;
|
|
};
|
|
|
|
static int wcd_gpio_read(struct wcd_gpio_priv *priv_data,
|
|
struct wcd_gpio_pad *pad, unsigned int addr)
|
|
{
|
|
unsigned int val;
|
|
int ret;
|
|
|
|
ret = regmap_read(priv_data->map, addr, &val);
|
|
if (ret < 0)
|
|
dev_err(priv_data->dev, "%s: read 0x%x failed\n",
|
|
__func__, addr);
|
|
else
|
|
ret = (val >> pad->offset);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int wcd_gpio_write(struct wcd_gpio_priv *priv_data,
|
|
struct wcd_gpio_pad *pad, unsigned int addr,
|
|
unsigned int val)
|
|
{
|
|
int ret;
|
|
|
|
ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset),
|
|
val << pad->offset);
|
|
if (ret < 0)
|
|
dev_err(priv_data->dev, "write 0x%x failed\n", addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int wcd_get_groups_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
return pctldev->desc->npins;
|
|
}
|
|
|
|
static const char *wcd_get_group_name(struct pinctrl_dev *pctldev,
|
|
unsigned int pin)
|
|
{
|
|
return pctldev->desc->pins[pin].name;
|
|
}
|
|
|
|
static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin,
|
|
const unsigned int **pins, unsigned int *num_pins)
|
|
{
|
|
*pins = &pctldev->desc->pins[pin].number;
|
|
*num_pins = 1;
|
|
return 0;
|
|
}
|
|
|
|
static const struct pinctrl_ops wcd_pinctrl_ops = {
|
|
.get_groups_count = wcd_get_groups_count,
|
|
.get_group_name = wcd_get_group_name,
|
|
.get_group_pins = wcd_get_group_pins,
|
|
.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
|
|
.dt_free_map = pinctrl_utils_free_map,
|
|
};
|
|
|
|
static int wcd_config_get(struct pinctrl_dev *pctldev,
|
|
unsigned int pin, unsigned long *config)
|
|
{
|
|
unsigned int param = pinconf_to_config_param(*config);
|
|
struct wcd_gpio_pad *pad;
|
|
unsigned int arg;
|
|
|
|
pad = pctldev->desc->pins[pin].drv_data;
|
|
|
|
switch (param) {
|
|
case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
arg = pad->pullup == WCD_GPIO_PULL_DOWN;
|
|
break;
|
|
case PIN_CONFIG_BIAS_DISABLE:
|
|
arg = pad->pullup = WCD_GPIO_BIAS_DISABLE;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_UP:
|
|
arg = pad->pullup == WCD_GPIO_PULL_UP;
|
|
break;
|
|
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
|
|
arg = !pad->is_valid;
|
|
break;
|
|
case PIN_CONFIG_INPUT_ENABLE:
|
|
arg = pad->output_enabled;
|
|
break;
|
|
case PIN_CONFIG_OUTPUT:
|
|
arg = pad->value;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
*config = pinconf_to_config_packed(param, arg);
|
|
return 0;
|
|
}
|
|
|
|
static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
|
|
unsigned long *configs, unsigned int nconfs)
|
|
{
|
|
struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev);
|
|
struct wcd_gpio_pad *pad;
|
|
unsigned int param, arg;
|
|
int i, ret;
|
|
|
|
pad = pctldev->desc->pins[pin].drv_data;
|
|
|
|
for (i = 0; i < nconfs; i++) {
|
|
param = pinconf_to_config_param(configs[i]);
|
|
arg = pinconf_to_config_argument(configs[i]);
|
|
|
|
dev_dbg(priv_data->dev, "%s: param: %d arg: %d",
|
|
__func__, param, arg);
|
|
|
|
switch (param) {
|
|
case PIN_CONFIG_BIAS_DISABLE:
|
|
pad->pullup = WCD_GPIO_BIAS_DISABLE;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_UP:
|
|
pad->pullup = WCD_GPIO_PULL_UP;
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
pad->pullup = WCD_GPIO_PULL_DOWN;
|
|
break;
|
|
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
|
|
pad->is_valid = false;
|
|
break;
|
|
case PIN_CONFIG_INPUT_ENABLE:
|
|
pad->output_enabled = false;
|
|
break;
|
|
case PIN_CONFIG_OUTPUT:
|
|
pad->output_enabled = true;
|
|
pad->value = arg;
|
|
break;
|
|
case PIN_CONFIG_DRIVE_STRENGTH:
|
|
pad->strength = arg;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (pad->output_enabled) {
|
|
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
|
|
pad->output_enabled);
|
|
if (ret < 0)
|
|
goto done;
|
|
ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL,
|
|
pad->value);
|
|
} else
|
|
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
|
|
pad->output_enabled);
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static const struct pinconf_ops wcd_pinconf_ops = {
|
|
.is_generic = true,
|
|
.pin_config_group_get = wcd_config_get,
|
|
.pin_config_group_set = wcd_config_set,
|
|
};
|
|
|
|
static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
|
|
{
|
|
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
|
unsigned long config;
|
|
|
|
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
|
|
|
|
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
|
|
}
|
|
|
|
static int wcd_gpio_direction_output(struct gpio_chip *chip,
|
|
unsigned int pin, int val)
|
|
{
|
|
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
|
unsigned long config;
|
|
|
|
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
|
|
|
|
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
|
|
}
|
|
|
|
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
|
{
|
|
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
|
struct wcd_gpio_pad *pad;
|
|
int value;
|
|
|
|
pad = priv_data->ctrl->desc->pins[pin].drv_data;
|
|
|
|
if (!pad->is_valid)
|
|
return -EINVAL;
|
|
|
|
value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL);
|
|
return value;
|
|
}
|
|
|
|
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
|
|
{
|
|
struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip);
|
|
unsigned long config;
|
|
|
|
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
|
|
|
|
wcd_config_set(priv_data->ctrl, pin, &config, 1);
|
|
}
|
|
|
|
static const struct gpio_chip wcd_gpio_chip = {
|
|
.direction_input = wcd_gpio_direction_input,
|
|
.direction_output = wcd_gpio_direction_output,
|
|
.get = wcd_gpio_get,
|
|
.set = wcd_gpio_set,
|
|
};
|
|
|
|
static int wcd_pinctrl_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct pinctrl_pin_desc *pindesc;
|
|
struct pinctrl_desc *pctrldesc;
|
|
struct wcd_gpio_pad *pad, *pads;
|
|
struct wcd_gpio_priv *priv_data;
|
|
int ret, i, j;
|
|
u32 npins;
|
|
char **name;
|
|
|
|
ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins);
|
|
if (ret) {
|
|
dev_err(dev, "%s: Looking up %s property in node %s failed\n",
|
|
__func__, "qcom,num-gpios", dev->of_node->full_name);
|
|
ret = -EINVAL;
|
|
goto err_priv_alloc;
|
|
}
|
|
if (!npins) {
|
|
dev_err(dev, "%s: no.of pins are 0\n", __func__);
|
|
ret = -EINVAL;
|
|
goto err_priv_alloc;
|
|
}
|
|
|
|
priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
|
|
if (!priv_data) {
|
|
ret = -ENOMEM;
|
|
goto err_priv_alloc;
|
|
}
|
|
|
|
priv_data->dev = dev;
|
|
priv_data->map = dev_get_regmap(dev->parent, NULL);
|
|
if (!priv_data->map) {
|
|
dev_err(dev, "%s: failed to get regmap\n", __func__);
|
|
ret = -EINVAL;
|
|
goto err_regmap;
|
|
}
|
|
|
|
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
|
|
if (!pindesc) {
|
|
ret = -ENOMEM;
|
|
goto err_pinsec_alloc;
|
|
}
|
|
|
|
pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
|
|
if (!pads) {
|
|
ret = -ENOMEM;
|
|
goto err_pads_alloc;
|
|
}
|
|
|
|
pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
|
|
if (!pctrldesc) {
|
|
ret = -ENOMEM;
|
|
goto err_pinctrl_alloc;
|
|
}
|
|
|
|
pctrldesc->pctlops = &wcd_pinctrl_ops;
|
|
pctrldesc->confops = &wcd_pinconf_ops;
|
|
pctrldesc->owner = THIS_MODULE;
|
|
pctrldesc->name = dev_name(dev);
|
|
pctrldesc->pins = pindesc;
|
|
pctrldesc->npins = npins;
|
|
|
|
name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL);
|
|
if (!name) {
|
|
ret = -ENOMEM;
|
|
goto err_name_alloc;
|
|
}
|
|
for (i = 0; i < npins; i++, pindesc++) {
|
|
name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN,
|
|
GFP_KERNEL);
|
|
if (!name[i]) {
|
|
ret = -ENOMEM;
|
|
goto err_pin;
|
|
}
|
|
pad = &pads[i];
|
|
pindesc->drv_data = pad;
|
|
pindesc->number = i;
|
|
snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1));
|
|
pindesc->name = name[i];
|
|
pad->offset = i;
|
|
pad->is_valid = true;
|
|
}
|
|
|
|
priv_data->chip = wcd_gpio_chip;
|
|
priv_data->chip.parent = dev;
|
|
priv_data->chip.base = -1;
|
|
priv_data->chip.ngpio = npins;
|
|
priv_data->chip.label = dev_name(dev);
|
|
priv_data->chip.of_gpio_n_cells = 2;
|
|
priv_data->chip.can_sleep = false;
|
|
|
|
priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data);
|
|
if (IS_ERR(priv_data->ctrl)) {
|
|
dev_err(dev, "%s: failed to register to pinctrl\n", __func__);
|
|
ret = PTR_ERR(priv_data->ctrl);
|
|
goto err_pin;
|
|
}
|
|
|
|
ret = gpiochip_add_data(&priv_data->chip, priv_data);
|
|
if (ret) {
|
|
dev_err(dev, "%s: can't add gpio chip\n", __func__);
|
|
goto err_pin;
|
|
}
|
|
|
|
ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0,
|
|
npins);
|
|
if (ret) {
|
|
dev_err(dev, "%s: failed to add pin range\n", __func__);
|
|
goto err_range;
|
|
}
|
|
platform_set_drvdata(pdev, priv_data);
|
|
|
|
return 0;
|
|
|
|
err_range:
|
|
gpiochip_remove(&priv_data->chip);
|
|
err_pin:
|
|
for (j = 0; j < i; j++)
|
|
devm_kfree(dev, name[j]);
|
|
devm_kfree(dev, name);
|
|
err_name_alloc:
|
|
devm_kfree(dev, pctrldesc);
|
|
err_pinctrl_alloc:
|
|
devm_kfree(dev, pads);
|
|
err_pads_alloc:
|
|
devm_kfree(dev, pindesc);
|
|
err_pinsec_alloc:
|
|
err_regmap:
|
|
devm_kfree(dev, priv_data);
|
|
err_priv_alloc:
|
|
return ret;
|
|
}
|
|
|
|
static int wcd_pinctrl_remove(struct platform_device *pdev)
|
|
{
|
|
struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev);
|
|
|
|
gpiochip_remove(&priv_data->chip);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id wcd_pinctrl_of_match[] = {
|
|
{ .compatible = "qcom,wcd-pinctrl" },
|
|
{ },
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match);
|
|
|
|
static struct platform_driver wcd_pinctrl_driver = {
|
|
.driver = {
|
|
.name = "qcom-wcd-pinctrl",
|
|
.of_match_table = wcd_pinctrl_of_match,
|
|
},
|
|
.probe = wcd_pinctrl_probe,
|
|
.remove = wcd_pinctrl_remove,
|
|
};
|
|
|
|
module_platform_driver(wcd_pinctrl_driver);
|
|
|
|
MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver");
|
|
MODULE_LICENSE("GPL v2");
|