From: zwl Date: Sun, 20 Jul 2014 11:43:13 +0000 (+0800) Subject: rk31xx lvds: create rk31xx lvds driver that is origin version X-Git-Tag: firefly_0821_release~4916^2~199 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=ad3b81d90672651255adb20ca34659a435059d17;p=firefly-linux-kernel-4.4.55.git rk31xx lvds: create rk31xx lvds driver that is origin version --- diff --git a/drivers/video/rockchip/transmitter/rk31xx_lvds.c b/drivers/video/rockchip/transmitter/rk31xx_lvds.c new file mode 100755 index 000000000000..4c0342268b78 --- /dev/null +++ b/drivers/video/rockchip/transmitter/rk31xx_lvds.c @@ -0,0 +1,295 @@ +/* + * drivers/video/rockchip/lcdc/rk31xx_lcdc.c + * + * Copyright (C) 2014 ROCKCHIP, Inc. + * Author: zhuangwenlong + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk31xx_lvds.h" + + +#define grf_readl(offset) readl_relaxed(RK_GRF_VIRT + offset) +#define grf_writel(v,offset) \ + do { \ + writel_relaxed(v, RK_GRF_VIRT + offset); \ + dsb(); \ + } while (0) + + +static struct rk_lvds_device *rk31xx_lvds; + +static int rk31xx_lvds_clk_init(struct rk_lvds_device *lvds) +{ + lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds"); + if (IS_ERR(lvds->pclk)) { + dev_err(lvds->dev, "get clk failed\n"); + return PTR_ERR(lvds->pclk); + } + + lvds->pd = devm_clk_get(lvds->dev,"pd_lvds"); + if (IS_ERR(lvds->pd)) { + dev_err(lvds->dev, "get clk failed\n"); + return PTR_ERR(lvds->pd); + } + return 0; +} + +static int rk31xx_lvds_clk_enable(struct rk_lvds_device *lvds) +{ + if (!lvds->clk_on) { + clk_prepare_enable(lvds->pd); + clk_prepare_enable(lvds->pclk); + lvds->clk_on = true; + } + + return 0; +} + +static int rk31xx_lvds_clk_disable(struct rk_lvds_device *lvds) +{ + if (lvds->clk_on) { + clk_disable_unprepare(lvds->pclk); + clk_disable_unprepare(lvds->pd); + lvds->clk_on = false; + } + + return 0; +} + +static int rk31xx_lvds_disable(void) +{ + struct rk_lvds_device *lvds = rk31xx_lvds; + + grf_writel(v_LVDSMODE_EN(0), RK31XX_GRF_LVDS_CON0); + /* power down lvds pll and bandgap */ + lvds_msk_reg(lvds, MIPIPHY_REGEA, m_BG_POWER_DOWN | m_PLL_POWER_DOWN, + v_BG_POWER_DOWN(0) | v_PLL_POWER_DOWN(0)); + /* disable lvds */ + lvds_msk_reg(lvds, MIPIPHY_REGE3, m_LVDS_EN, v_LVDS_EN(0)); + + rk31xx_lvds_clk_disable(lvds); + return 0; +} + +static void rk31xx_output_lvds(struct rk_lvds_device *lvds, + struct rk_screen *screen) +{ + u32 val = 0; + + /* if LVDS transmitter source from VOP, vop_dclk need get invert + * set iomux in dts pinctrl + */ + val = 0; + val |= v_LVDSMODE_EN(1) | v_MIPIPHY_TTL_EN(0); /* enable lvds mode */ + val |= v_LVDS_DATA_SEL(LVDS_DATA_FROM_LCDC); /* config data source */ + val |= v_LVDS_OUTPUT_FORMAT(screen->lvds_format); /* config lvds_format */ + val |= v_LVDS_MSBSEL(LVDS_MSB_D7); /* LSB receive mode */ + grf_writel(val, RK31XX_GRF_LVDS_CON0); + + /* enable lvds lane */ + val = v_LANE0_EN(1) | v_LANE1_EN(1) | v_LANE2_EN(1) | v_LANE3_EN(1) | + v_LANECLK_EN(1); + lvds_writel(lvds, MIPIPHY_REG0, val); + + /* set pll prediv and fbdiv */ + lvds_writel(lvds, MIPIPHY_REG3, v_PREDIV(1) | v_FBDIV_MSB(0)); + lvds_writel(lvds, MIPIPHY_REG4, v_FBDIV_LSB(7)); + + /* set lvds mode and reset phy config */ + val = v_LVDS_MODE_EN(1) | v_TTL_MODE_EN(0) | v_MIPI_MODE_EN(0) | + v_MSB_SEL(1) | v_DIG_INTER_RST(1); + lvds_writel(lvds, MIPIPHY_REGE0, val); + + lvds_writel(lvds, MIPIPHY_REGE1, 0x92); +#if 0 + lvds_writel(lvds, MIPIPHY_REGE2, 0xa0); /* timing */ + lvds_writel(lvds, MIPIPHY_REGE7, 0xfc); /* phase */ +#endif + /* power up lvds pll and bandgap */ + lvds_msk_reg(lvds, MIPIPHY_REGEA, m_BG_POWER_DOWN | m_PLL_POWER_DOWN, + v_BG_POWER_DOWN(0) | v_PLL_POWER_DOWN(0)); /* 0xf8 */ + + /* enable lvds */ + lvds_msk_reg(lvds, MIPIPHY_REGE3, m_MIPI_EN | m_LVDS_EN | m_TTL_EN, + v_MIPI_EN(0) | v_LVDS_EN(1) | v_TTL_EN(0)); + +} + +static void rk31xx_output_lvttl(struct rk_lvds_device *lvds, + struct rk_screen *screen) +{ + u32 val = 0; + + val |= v_LVDSMODE_EN(0) | v_MIPIPHY_TTL_EN(1); /* enable lvds mode */ + val |= v_LVDS_DATA_SEL(LVDS_DATA_FROM_LCDC); /* config data source */ + grf_writel(val, RK31XX_GRF_LVDS_CON0); + + /* set pll prediv and fbdiv */ + lvds_writel(lvds, MIPIPHY_REG3, v_PREDIV(1) | v_FBDIV_MSB(0)); + lvds_writel(lvds, MIPIPHY_REG4, v_FBDIV_LSB(7)); + + /* set lvds mode and reset phy config */ + val = v_LVDS_MODE_EN(0) | v_TTL_MODE_EN(1) | v_MIPI_MODE_EN(0) | + v_MSB_SEL(1) | v_DIG_INTER_RST(1); + lvds_writel(lvds, MIPIPHY_REGE0, val); + + lvds_writel(lvds, MIPIPHY_REGE1, 0x92); + + /* enable ttl */ + lvds_msk_reg(lvds, MIPIPHY_REGE3, m_MIPI_EN | m_LVDS_EN | m_TTL_EN, + v_MIPI_EN(0) | v_LVDS_EN(0) | v_TTL_EN(1)); + +} + +static int rk31xx_lvds_en(void) +{ + struct rk_lvds_device *lvds = rk31xx_lvds; + struct rk_screen *screen = &lvds->screen; + + rk_fb_get_prmry_screen(screen); + + /* enable clk */ + rk31xx_lvds_clk_enable(lvds); + + switch (screen->type) { + case SCREEN_LVDS: + rk31xx_output_lvds(lvds, screen); + break; + case SCREEN_RGB: + rk31xx_output_lvttl(lvds, screen); + break; + default: + printk("unsupport screen type\n"); + break; + } + + return 0; +} + +static struct rk_fb_trsm_ops trsm_lvds_ops = { + .enable = rk31xx_lvds_en, + .disable = rk31xx_lvds_disable, +}; + +static int rk31xx_lvds_probe(struct platform_device *pdev) +{ + struct rk_lvds_device *lvds; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + int ret = 0; + + if (!np) { + dev_err(&pdev->dev, "Don't find lvds device tree node.\n"); + return -EINVAL; + } + + lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk_lvds_device), GFP_KERNEL); + if (!lvds) { + dev_err(&pdev->dev, "kzalloc rk31xx lvds failed\n"); + return -ENOMEM; + } + lvds->dev = &pdev->dev; + + rk_fb_get_prmry_screen(&lvds->screen); + if ((lvds->screen.type != SCREEN_RGB) && + (lvds->screen.type != SCREEN_LVDS) && + (lvds->screen.type != SCREEN_DUAL_LVDS)) { + dev_err(&pdev->dev, "screen is not lvds/rgb!\n"); + ret = -EINVAL; + goto err_screen_type; + } + + platform_set_drvdata(pdev, lvds); + dev_set_name(lvds->dev, "rk31xx-lvds"); + + /* lvds regs on MIPIPHY_REG */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lvds->regbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(lvds->regbase)) { + dev_err(&pdev->dev, "ioremap reg failed\n"); + return PTR_ERR(lvds->regbase); + } + + ret = rk31xx_lvds_clk_init(lvds); + if(ret < 0) + goto err_clk_init; + + if (support_uboot_display()) + rk31xx_lvds_clk_enable(lvds); + + rk31xx_lvds = lvds; + rk_fb_trsm_ops_register(&trsm_lvds_ops, SCREEN_LVDS); + dev_info(&pdev->dev, "rk31xx lvds driver probe success\n"); + + return 0; + +err_clk_init: +err_screen_type: + devm_kfree(&pdev->dev, lvds); + lvds = NULL; + return ret; +} + +static int rk31xx_lvds_remove(struct platform_device *pdev) +{ + return 0; +} + +static void rk31xx_lvds_shutdown(struct platform_device *pdev) +{ + return; +} + +#if defined(CONFIG_OF) +static const struct of_device_id rk31xx_lvds_dt_ids[] = { + {.compatible = "rockchip,rk31xx-lvds",}, + {} +}; +#endif + +static struct platform_driver rk31xx_lvds_driver = { + .driver = { + .name = "rk31xx-lvds", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(rk31xx_lvds_dt_ids), +#endif + }, + .probe = rk31xx_lvds_probe, + .remove = rk31xx_lvds_remove, + .shutdown = rk31xx_lvds_shutdown, +}; + +static int __init rk31xx_lvds_init(void) +{ + return platform_driver_register(&rk31xx_lvds_driver); +} + +static void __exit rk31xx_lvds_exit(void) +{ + platform_driver_unregister(&rk31xx_lvds_driver); +} + +fs_initcall(rk31xx_lvds_init); +module_exit(rk31xx_lvds_exit); + diff --git a/drivers/video/rockchip/transmitter/rk31xx_lvds.h b/drivers/video/rockchip/transmitter/rk31xx_lvds.h new file mode 100755 index 000000000000..b613e6da6981 --- /dev/null +++ b/drivers/video/rockchip/transmitter/rk31xx_lvds.h @@ -0,0 +1,110 @@ +#ifndef _RK31XX_LVDS_H_ +#define _RK31XX_LVDS_H_ + +#include + +#ifdef BIT +#undef BIT +#endif +#define BIT(x, bit) ((x) << (bit)) + +#ifdef BIT_MASK +#undef BIT_MASK +#endif +#define BIT_MASK(x, mask, bit) BIT((x) & (mask), bit) +#define BIT_EN(mask, bit) BIT(mask, bit + 16) + +#define RK31XX_GRF_LVDS_CON0 0x0150 +#define v_LVDS_DATA_SEL(x) (BIT_MASK(x, 1, 0) | BIT_EN(1, 0)) +#define v_LVDS_OUTPUT_FORMAT(x) (BIT_MASK(x, 3, 1) | BIT_EN(3, 1)) +#define v_LVDS_MSBSEL(x) (BIT_MASK(x, 1, 3) | BIT_EN(1, 3)) +#define v_LVDSMODE_EN(x) (BIT_MASK(x, 1, 6) | BIT_EN(1, 6)) +#define v_MIPIPHY_TTL_EN(x) (BIT_MASK(x, 1, 7) | BIT_EN(1, 7)) + +enum { + LVDS_DATA_FROM_LCDC = 0, + LVDS_DATA_FORM_EBC, +}; + +enum { + LVDS_MSB_D0 = 0, + LVDS_MSB_D7, +}; + +#define MIPIPHY_REG0 0x0000 +#define v_LANE0_EN(x) BIT_MASK(x, 1, 2) +#define v_LANE1_EN(x) BIT_MASK(x, 1, 3) +#define v_LANE2_EN(x) BIT_MASK(x, 1, 4) +#define v_LANE3_EN(x) BIT_MASK(x, 1, 5) +#define v_LANECLK_EN(x) BIT_MASK(x, 1, 6) + +#define MIPIPHY_REG3 0x000c +#define m_PREDIV BIT(0x1f, 0) +#define m_FBDIV_MSB BIT(1, 5) +#define v_PREDIV(x) BIT_MASK(x, 0x1f, 0) +#define v_FBDIV_MSB(x) BIT_MASK(x, 1, 5) + +#define MIPIPHY_REG4 0x0010 +#define v_FBDIV_LSB(x) BIT_MASK(x, 0xff, 0) + +#define MIPIPHY_REGE0 0x0380 +#define m_MSB_SEL BIT(1, 0) +#define m_DIG_INTER_RST BIT(1, 2) +#define m_LVDS_MODE_EN BIT(1, 5) +#define m_TTL_MODE_EN BIT(1, 6) +#define m_MIPI_MODE_EN BIT(1, 7) +#define v_MSB_SEL(x) BIT_MASK(x, 1, 0) +#define v_DIG_INTER_RST(x) BIT_MASK(x, 1, 2) +#define v_LVDS_MODE_EN(x) BIT_MASK(x, 1, 5) +#define v_TTL_MODE_EN(x) BIT_MASK(x, 1, 6) +#define v_MIPI_MODE_EN(x) BIT_MASK(x, 1, 7) + +#define MIPIPHY_REGE1 0x0384 +#define m_DIG_INTER_EN BIT(1, 7) +#define v_DIG_INTER_EN(x) BIT_MASK(x, 1, 7) + +#define MIPIPHY_REGE3 0x038c +#define m_MIPI_EN BIT(1, 0) +#define m_LVDS_EN BIT(1, 1) +#define m_TTL_EN BIT(1, 2) +#define v_MIPI_EN(x) BIT_MASK(x, 1, 0) +#define v_LVDS_EN(x) BIT_MASK(x, 1, 1) +#define v_TTL_EN(x) BIT_MASK(x, 1, 2) + +#define MIPIPHY_REGEA 0x03A8 +#define m_BG_POWER_DOWN BIT(1, 0) +#define m_PLL_POWER_DOWN BIT(1, 2) +#define v_BG_POWER_DOWN(x) BIT_MASK(x, 1, 0) +#define v_PLL_POWER_DOWN(x) BIT_MASK(x, 1, 2) + +#define MIPIPHY_REGE2 0x0388 +#define MIPIPHY_REGE7 0x039C + + +struct rk_lvds_device { + struct device *dev; + void __iomem *regbase; + struct clk *pclk; /*phb clk*/ + struct clk *pd; + struct rk_screen screen; + bool clk_on; +}; + +static int inline lvds_writel(struct rk_lvds_device *lvds, u32 offset, u32 val) +{ + writel_relaxed(val, lvds->regbase + offset); + return 0; +} + +static inline int lvds_msk_reg(struct rk_lvds_device *lvds, u32 offset, + u32 msk, u32 val) +{ + u32 temp; + + temp = readl_relaxed(lvds->regbase + offset) & (0xFF - (msk)); + writel_relaxed(temp | ((val) & (msk)), lvds->regbase + offset); + return 0; +} + +#endif +