| /* |
| * Copyright (C) STMicroelectronics SA 2014 |
| * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. |
| * License terms: GNU General Public License (GPL), version 2 |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/io.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/platform_device.h> |
| |
| #include <drm/drmP.h> |
| |
| /* registers offset */ |
| #define VTAC_CONFIG 0x00 |
| #define VTAC_RX_FIFO_CONFIG 0x04 |
| #define VTAC_FIFO_CONFIG_VAL 0x04 |
| |
| #define VTAC_SYS_CFG8521 0x824 |
| #define VTAC_SYS_CFG8522 0x828 |
| |
| /* Number of phyts per pixel */ |
| #define VTAC_2_5_PPP 0x0005 |
| #define VTAC_3_PPP 0x0006 |
| #define VTAC_4_PPP 0x0008 |
| #define VTAC_5_PPP 0x000A |
| #define VTAC_6_PPP 0x000C |
| #define VTAC_13_PPP 0x001A |
| #define VTAC_14_PPP 0x001C |
| #define VTAC_15_PPP 0x001E |
| #define VTAC_16_PPP 0x0020 |
| #define VTAC_17_PPP 0x0022 |
| #define VTAC_18_PPP 0x0024 |
| |
| /* enable bits */ |
| #define VTAC_ENABLE 0x3003 |
| |
| #define VTAC_TX_PHY_ENABLE_CLK_PHY BIT(0) |
| #define VTAC_TX_PHY_ENABLE_CLK_DLL BIT(1) |
| #define VTAC_TX_PHY_PLL_NOT_OSC_MODE BIT(3) |
| #define VTAC_TX_PHY_RST_N_DLL_SWITCH BIT(4) |
| #define VTAC_TX_PHY_PROG_N3 BIT(9) |
| |
| |
| /** |
| * VTAC mode structure |
| * |
| * @vid_in_width: Video Data Resolution |
| * @phyts_width: Width of phyt buses(phyt low and phyt high). |
| * @phyts_per_pixel: Number of phyts sent per pixel |
| */ |
| struct sti_vtac_mode { |
| u32 vid_in_width; |
| u32 phyts_width; |
| u32 phyts_per_pixel; |
| }; |
| |
| static const struct sti_vtac_mode vtac_mode_main = {0x2, 0x2, VTAC_5_PPP}; |
| static const struct sti_vtac_mode vtac_mode_aux = {0x1, 0x0, VTAC_17_PPP}; |
| |
| /** |
| * VTAC structure |
| * |
| * @dev: pointer to device structure |
| * @regs: ioremapped registers for RX and TX devices |
| * @phy_regs: phy registers for TX device |
| * @clk: clock |
| * @mode: main or auxillary configuration mode |
| */ |
| struct sti_vtac { |
| struct device *dev; |
| void __iomem *regs; |
| void __iomem *phy_regs; |
| struct clk *clk; |
| const struct sti_vtac_mode *mode; |
| }; |
| |
| static void sti_vtac_rx_set_config(struct sti_vtac *vtac) |
| { |
| u32 config; |
| |
| /* Enable VTAC clock */ |
| if (clk_prepare_enable(vtac->clk)) |
| DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n"); |
| |
| writel(VTAC_FIFO_CONFIG_VAL, vtac->regs + VTAC_RX_FIFO_CONFIG); |
| |
| config = VTAC_ENABLE; |
| config |= vtac->mode->vid_in_width << 4; |
| config |= vtac->mode->phyts_width << 16; |
| config |= vtac->mode->phyts_per_pixel << 23; |
| writel(config, vtac->regs + VTAC_CONFIG); |
| } |
| |
| static void sti_vtac_tx_set_config(struct sti_vtac *vtac) |
| { |
| u32 phy_config; |
| u32 config; |
| |
| /* Enable VTAC clock */ |
| if (clk_prepare_enable(vtac->clk)) |
| DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n"); |
| |
| /* Configure vtac phy */ |
| phy_config = 0x00000000; |
| writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8522); |
| phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY; |
| writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config |= VTAC_TX_PHY_PROG_N3; |
| writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL; |
| writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH; |
| writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); |
| phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE; |
| writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); |
| |
| /* Configure vtac tx */ |
| config = VTAC_ENABLE; |
| config |= vtac->mode->vid_in_width << 4; |
| config |= vtac->mode->phyts_width << 16; |
| config |= vtac->mode->phyts_per_pixel << 23; |
| writel(config, vtac->regs + VTAC_CONFIG); |
| } |
| |
| static const struct of_device_id vtac_of_match[] = { |
| { |
| .compatible = "st,vtac-main", |
| .data = &vtac_mode_main, |
| }, { |
| .compatible = "st,vtac-aux", |
| .data = &vtac_mode_aux, |
| }, { |
| /* end node */ |
| } |
| }; |
| MODULE_DEVICE_TABLE(of, vtac_of_match); |
| |
| static int sti_vtac_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct device_node *np = dev->of_node; |
| const struct of_device_id *id; |
| struct sti_vtac *vtac; |
| struct resource *res; |
| |
| vtac = devm_kzalloc(dev, sizeof(*vtac), GFP_KERNEL); |
| if (!vtac) |
| return -ENOMEM; |
| |
| vtac->dev = dev; |
| |
| id = of_match_node(vtac_of_match, np); |
| if (!id) |
| return -ENOMEM; |
| |
| vtac->mode = id->data; |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| DRM_ERROR("Invalid resource\n"); |
| return -ENOMEM; |
| } |
| vtac->regs = devm_ioremap_resource(dev, res); |
| if (IS_ERR(vtac->regs)) |
| return PTR_ERR(vtac->regs); |
| |
| |
| vtac->clk = devm_clk_get(dev, "vtac"); |
| if (IS_ERR(vtac->clk)) { |
| DRM_ERROR("Cannot get vtac clock\n"); |
| return PTR_ERR(vtac->clk); |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| if (res) { |
| vtac->phy_regs = devm_ioremap_nocache(dev, res->start, |
| resource_size(res)); |
| sti_vtac_tx_set_config(vtac); |
| } else { |
| |
| sti_vtac_rx_set_config(vtac); |
| } |
| |
| platform_set_drvdata(pdev, vtac); |
| DRM_INFO("%s %s\n", __func__, dev_name(vtac->dev)); |
| |
| return 0; |
| } |
| |
| static int sti_vtac_remove(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| |
| struct platform_driver sti_vtac_driver = { |
| .driver = { |
| .name = "sti-vtac", |
| .owner = THIS_MODULE, |
| .of_match_table = vtac_of_match, |
| }, |
| .probe = sti_vtac_probe, |
| .remove = sti_vtac_remove, |
| }; |
| |
| module_platform_driver(sti_vtac_driver); |
| |
| MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); |
| MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); |
| MODULE_LICENSE("GPL"); |