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