190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart/* 290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder 390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * 436d50464e05f498fa4024270e091b306af5de898Laurent Pinchart * Copyright (C) 2013-2014 Renesas Electronics Corporation 590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * 690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * 890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * This program is free software; you can redistribute it and/or modify 990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * it under the terms of the GNU General Public License as published by 1090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * the Free Software Foundation; either version 2 of the License, or 1190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * (at your option) any later version. 1290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart */ 1390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 1490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include <linux/clk.h> 1590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include <linux/delay.h> 1690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include <linux/io.h> 1790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include <linux/platform_device.h> 1890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include <linux/slab.h> 1990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 2090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include "rcar_du_drv.h" 2190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include "rcar_du_encoder.h" 2290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include "rcar_du_lvdsenc.h" 2390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart#include "rcar_lvds_regs.h" 2490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 2590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartstruct rcar_du_lvdsenc { 2690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct rcar_du_device *dev; 2790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 2890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart unsigned int index; 2990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart void __iomem *mmio; 3090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct clk *clock; 3190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart int dpms; 3290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 3390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart enum rcar_lvds_input input; 3490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart}; 3590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 3690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartstatic void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) 3790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart{ 3890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart iowrite32(data, lvds->mmio + reg); 3990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart} 4090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 4190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartstatic int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, 4290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct rcar_du_crtc *rcrtc) 4390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart{ 4490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart const struct drm_display_mode *mode = &rcrtc->crtc.mode; 4590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart unsigned int freq = mode->clock; 4690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart u32 lvdcr0; 475cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart u32 lvdhcr; 4890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart u32 pllcr; 4990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart int ret; 5090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 5190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (lvds->dpms == DRM_MODE_DPMS_ON) 5290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return 0; 5390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 5490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart ret = clk_prepare_enable(lvds->clock); 5590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (ret < 0) 5690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return ret; 5790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 5890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart /* PLL clock configuration */ 5990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (freq <= 38000) 6090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; 6190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart else if (freq <= 60000) 6290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; 6390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart else if (freq <= 121000) 6490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; 6590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart else 6690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart pllcr = LVDPLLCR_PLLDLYCNT_150M; 6790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 6890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDPLLCR, pllcr); 6990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 7090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart /* Hardcode the channels and control signals routing for now. 7190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * 7290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * HSYNC -> CTRL0 7390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * VSYNC -> CTRL1 7490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * DISP -> CTRL2 7590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * 0 -> CTRL3 7690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart */ 7790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | 7890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | 7990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart LVDCTRCR_CTR0SEL_HSYNC); 805cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart 815cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES)) 825cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) 835cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1); 845cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart else 855cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1) 865cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3); 875cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart 885cca30ebe089be23bc2588c4b76af69e89b3b639Laurent Pinchart rcar_lvds_write(lvds, LVDCHCR, lvdhcr); 8990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 9090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart /* Select the input, hardcode mode 0, enable LVDS operation and turn 9190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * bias circuitry on. 9290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart */ 9390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; 9490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (rcrtc->index == 2) 9590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvdcr0 |= LVDCR0_DUSEL; 9690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCR0, lvdcr0); 9790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 9890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart /* Turn all the channels on. */ 9990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | 10090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); 10190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 10290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart /* Turn the PLL on, wait for the startup delay, and turn the output 10390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart * on. 10490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart */ 10590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvdcr0 |= LVDCR0_PLLEN; 10690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCR0, lvdcr0); 10790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 10890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart usleep_range(100, 150); 10990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 11090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvdcr0 |= LVDCR0_LVRES; 11190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCR0, lvdcr0); 11290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 11390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->dpms = DRM_MODE_DPMS_ON; 11490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return 0; 11590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart} 11690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 11790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartstatic void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) 11890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart{ 11990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (lvds->dpms == DRM_MODE_DPMS_OFF) 12090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return; 12190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 12290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCR0, 0); 12390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_lvds_write(lvds, LVDCR1, 0); 12490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 12590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart clk_disable_unprepare(lvds->clock); 12690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 12790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->dpms = DRM_MODE_DPMS_OFF; 12890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart} 12990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 13090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartint rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, 13190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct drm_crtc *crtc, int mode) 13290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart{ 13390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (mode == DRM_MODE_DPMS_OFF) { 13490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcar_du_lvdsenc_stop(lvds); 13590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return 0; 13690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart } else if (crtc) { 13790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); 13890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return rcar_du_lvdsenc_start(lvds, rcrtc); 13990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart } else 14090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return -EINVAL; 14190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart} 14290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 14390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartstatic int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, 14490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct platform_device *pdev) 14590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart{ 14690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct resource *mem; 14790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart char name[7]; 14890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 14990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart sprintf(name, "lvds.%u", lvds->index); 15090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 15190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); 15290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->mmio = devm_ioremap_resource(&pdev->dev, mem); 153c8ca9d6aa676b7a26ae9dce6e50222519380e483Wei Yongjun if (IS_ERR(lvds->mmio)) 154c8ca9d6aa676b7a26ae9dce6e50222519380e483Wei Yongjun return PTR_ERR(lvds->mmio); 15590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 15690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->clock = devm_clk_get(&pdev->dev, name); 15790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (IS_ERR(lvds->clock)) { 15890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart dev_err(&pdev->dev, "failed to get clock for %s\n", name); 15990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return PTR_ERR(lvds->clock); 16090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart } 16190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 16290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return 0; 16390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart} 16490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 16590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchartint rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) 16690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart{ 16790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct platform_device *pdev = to_platform_device(rcdu->dev); 16890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart struct rcar_du_lvdsenc *lvds; 16990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart unsigned int i; 17090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart int ret; 17190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 17290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart for (i = 0; i < rcdu->info->num_lvds; ++i) { 17390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); 17490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (lvds == NULL) { 17590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart dev_err(&pdev->dev, "failed to allocate private data\n"); 17690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return -ENOMEM; 17790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart } 17890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 17990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->dev = rcdu; 18090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->index = i; 18190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0; 18290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart lvds->dpms = DRM_MODE_DPMS_OFF; 18390374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 18490374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart ret = rcar_du_lvdsenc_get_resources(lvds, pdev); 18590374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart if (ret < 0) 18690374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return ret; 18790374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 18890374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart rcdu->lvds[i] = lvds; 18990374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart } 19090374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart 19190374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart return 0; 19290374b5c25c9f04895c52a1e7a2468ee8dac525bLaurent Pinchart} 193