mfd: Add support for rk618

RK618 is a partner chip for Rockchip mobile application processor.

RK618 includes two RGB display input interface with double data rate.
With the internal MUX function, it can output 1080P HDMI signal to
TV and output RGB/LVDS/MIPI signal to TFT panel. In this case, RK618
can support dual panel (TV and TFT) display.

RK618 includes a audio codec, which with two I2S/PCM interface, two
differential microphone input and audio processing function.

Change-Id: Id18c251cbe1613de98e84c2b022826f85b3dd82b
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi 2017-07-09 10:01:18 +08:00 committed by Tao Huang
parent 46cf38cc9d
commit 2ef4ee93f4
5 changed files with 300 additions and 0 deletions

View File

@ -0,0 +1,31 @@
Device-Tree bindings for Rockchip RK618 MFD driver
Required properties:
- compatible: value should be one of the following:
"rockchip,rk618"
- reg: I2C device address.
- clocks: phandle to the clkin clock provider
- clock-names: Must be "clkin"
- reset-gpios: a GPIO spec for the reset pin
Optional properties:
- power-supply: regulator to provide the supply voltage
- enable-gpios: a GPIO spec for the enable pin
Example:
&i2c5 {
status = "okay";
rk618: rk618@50 {
compatible = "rockchip,rk618";
reg = <0x50>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2s_8ch_mclk &lcdc_lcdc &state_video_phy_ttl>;
pinctrl-1 = <&lcdc_gpio>;
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-names = "clkin";
reset-gpios = <&gpio3 14 GPIO_ACTIVE_LOW>;
};
};

View File

@ -778,6 +778,16 @@ config MFD_RC5T583
Additional drivers must be enabled in order to use the
different functionality of the device.
config MFD_RK618
tristate "Rockchip RK618 MFD Driver"
depends on I2C
depends on OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
if you say yes here you get support for the RK618 from Rockchip.
config MFD_RK808
tristate "Rockchip RK808 Power Management chip"
depends on I2C && OF

View File

@ -173,6 +173,7 @@ obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_RK618) += rk618.o
obj-$(CONFIG_MFD_RK808) += rk808.o
obj-$(CONFIG_MFD_RN5T618) += rn5t618.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o

225
drivers/mfd/rk618.c Normal file
View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/core.h>
#include <linux/mfd/rk618.h>
static const struct mfd_cell rk618_devs[] = {
{
.name = "rk618-cru",
.of_compatible = "rockchip,rk618-cru",
}, {
.name = "rk618-hdmi",
.of_compatible = "rockchip,rk618-hdmi",
}, {
.name = "rk618-lvds",
.of_compatible = "rockchip,rk618-lvds",
}, {
.name = "rk618-rgb",
.of_compatible = "rockchip,rk618-rgb",
}, {
.name = "rk618-mipi-dphy",
.of_compatible = "rockchip,rk618-mipi-dphy",
}, {
.name = "rk618-mipi-dsi",
.of_compatible = "rockchip,rk618-mipi-dsi",
}, {
.name = "rk618-codec",
.of_compatible = "rockchip,rk618-codec",
},
};
static int rk618_power_on(struct rk618 *rk618)
{
int ret;
ret = regulator_enable(rk618->supply);
if (ret < 0) {
dev_err(rk618->dev, "failed to enable supply: %d\n", ret);
return ret;
}
if (rk618->enable_gpio)
gpiod_direction_output(rk618->enable_gpio, 1);
usleep_range(1000, 2000);
gpiod_direction_output(rk618->reset_gpio, 0);
usleep_range(2000, 4000);
gpiod_direction_output(rk618->reset_gpio, 1);
usleep_range(50000, 60000);
gpiod_direction_output(rk618->reset_gpio, 0);
return 0;
}
static void rk618_power_off(struct rk618 *rk618)
{
gpiod_direction_output(rk618->reset_gpio, 1);
if (rk618->enable_gpio)
gpiod_direction_output(rk618->enable_gpio, 0);
regulator_disable(rk618->supply);
}
static const struct regmap_config rk618_regmap_config = {
.name = "core",
.reg_bits = 16,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0x9c,
.reg_format_endian = REGMAP_ENDIAN_NATIVE,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
};
static int
rk618_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct rk618 *rk618;
int ret;
rk618 = devm_kzalloc(dev, sizeof(*rk618), GFP_KERNEL);
if (!rk618)
return -ENOMEM;
rk618->dev = dev;
rk618->client = client;
i2c_set_clientdata(client, rk618);
rk618->supply = devm_regulator_get(dev, "power");
if (IS_ERR(rk618->supply))
return PTR_ERR(rk618->supply);
rk618->enable_gpio = devm_gpiod_get_optional(dev, "enable", 0);
if (IS_ERR(rk618->enable_gpio)) {
ret = PTR_ERR(rk618->enable_gpio);
dev_err(dev, "failed to request enable GPIO: %d\n", ret);
return ret;
}
rk618->reset_gpio = devm_gpiod_get(dev, "reset", 0);
if (IS_ERR(rk618->reset_gpio)) {
ret = PTR_ERR(rk618->reset_gpio);
dev_err(dev, "failed to request reset GPIO: %d\n", ret);
return ret;
}
rk618->clkin = devm_clk_get(dev, "clkin");
if (IS_ERR(rk618->clkin)) {
dev_err(dev, "failed to get clock\n");
return PTR_ERR(rk618->clkin);
}
ret = clk_prepare_enable(rk618->clkin);
if (ret) {
dev_err(dev, "unable to enable clock: %d\n", ret);
return ret;
}
ret = rk618_power_on(rk618);
if (ret)
goto err_clk_disable;
rk618->regmap = devm_regmap_init_i2c(client, &rk618_regmap_config);
if (IS_ERR(rk618->regmap)) {
ret = PTR_ERR(rk618->regmap);
dev_err(dev, "failed to allocate register map: %d\n", ret);
goto err_clk_disable;
}
ret = mfd_add_devices(dev, -1, rk618_devs, ARRAY_SIZE(rk618_devs),
NULL, 0, NULL);
if (ret) {
dev_err(dev, "failed to add subdev: %d\n", ret);
goto err_clk_disable;
}
return 0;
err_clk_disable:
clk_disable_unprepare(rk618->clkin);
return ret;
}
static int rk618_remove(struct i2c_client *client)
{
struct rk618 *rk618 = i2c_get_clientdata(client);
mfd_remove_devices(rk618->dev);
rk618_power_off(rk618);
clk_disable_unprepare(rk618->clkin);
return 0;
}
static void rk618_shutdown(struct i2c_client *client)
{
struct rk618 *rk618 = i2c_get_clientdata(client);
rk618_power_off(rk618);
clk_disable_unprepare(rk618->clkin);
}
static int __maybe_unused rk618_suspend(struct device *dev)
{
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int __maybe_unused rk618_resume(struct device *dev)
{
pinctrl_pm_select_default_state(dev);
return 0;
}
static const struct dev_pm_ops rk618_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(rk618_suspend, rk618_resume)
};
static const struct of_device_id rk618_of_match[] = {
{ .compatible = "rockchip,rk618", },
{}
};
MODULE_DEVICE_TABLE(of, rk618_of_match);
static const struct i2c_device_id rk618_i2c_id[] = {
{ "rk618", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, rk618_i2c_id);
static struct i2c_driver rk618_driver = {
.driver = {
.name = "rk618",
.of_match_table = of_match_ptr(rk618_of_match),
.pm = &rk618_pm_ops,
},
.probe = rk618_probe,
.remove = rk618_remove,
.shutdown = rk618_shutdown,
.id_table = rk618_i2c_id,
};
module_i2c_driver(rk618_driver);
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip rk618 i2c driver");
MODULE_LICENSE("GPL v2");

33
include/linux/mfd/rk618.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*/
#ifndef __RK618_H__
#define __RK618_H__
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/regmap.h>
struct rk618 {
struct device *dev;
struct i2c_client *client;
struct clk *clkin;
struct regmap *regmap;
struct regulator *supply;
struct gpio_desc *enable_gpio;
struct gpio_desc *reset_gpio; /* power on reset */
};
#endif