brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · a382dc4 Raw
404 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016 Broadcom Limited4 */5 6/**7 * DOC: VC4 DPI module8 *9 * The VC4 DPI hardware supports MIPI DPI type 4 and Nokia ViSSI10 * signals.  On BCM2835, these can be routed out to GPIO0-27 with the11 * ALT2 function.12 */13 14#include <drm/drm_atomic_helper.h>15#include <drm/drm_bridge.h>16#include <drm/drm_drv.h>17#include <drm/drm_edid.h>18#include <drm/drm_of.h>19#include <drm/drm_panel.h>20#include <drm/drm_probe_helper.h>21#include <drm/drm_simple_kms_helper.h>22#include <linux/clk.h>23#include <linux/component.h>24#include <linux/media-bus-format.h>25#include <linux/mod_devicetable.h>26#include <linux/platform_device.h>27#include "vc4_drv.h"28#include "vc4_regs.h"29 30#define DPI_C			0x0031# define DPI_OUTPUT_ENABLE_MODE		BIT(16)32 33/* The order field takes the incoming 24 bit RGB from the pixel valve34 * and shuffles the 3 channels.35 */36# define DPI_ORDER_MASK			VC4_MASK(15, 14)37# define DPI_ORDER_SHIFT		1438# define DPI_ORDER_RGB			039# define DPI_ORDER_BGR			140# define DPI_ORDER_GRB			241# define DPI_ORDER_BRG			342 43/* The format field takes the ORDER-shuffled pixel valve data and44 * formats it onto the output lines.45 */46# define DPI_FORMAT_MASK		VC4_MASK(13, 11)47# define DPI_FORMAT_SHIFT		1148/* This define is named in the hardware, but actually just outputs 0. */49# define DPI_FORMAT_9BIT_666_RGB	050/* Outputs 00000000rrrrrggggggbbbbb */51# define DPI_FORMAT_16BIT_565_RGB_1	152/* Outputs 000rrrrr00gggggg000bbbbb */53# define DPI_FORMAT_16BIT_565_RGB_2	254/* Outputs 00rrrrr000gggggg00bbbbb0 */55# define DPI_FORMAT_16BIT_565_RGB_3	356/* Outputs 000000rrrrrrggggggbbbbbb */57# define DPI_FORMAT_18BIT_666_RGB_1	458/* Outputs 00rrrrrr00gggggg00bbbbbb */59# define DPI_FORMAT_18BIT_666_RGB_2	560/* Outputs rrrrrrrrggggggggbbbbbbbb */61# define DPI_FORMAT_24BIT_888_RGB	662 63/* Reverses the polarity of the corresponding signal */64# define DPI_PIXEL_CLK_INVERT		BIT(10)65# define DPI_HSYNC_INVERT		BIT(9)66# define DPI_VSYNC_INVERT		BIT(8)67# define DPI_OUTPUT_ENABLE_INVERT	BIT(7)68 69/* Outputs the signal the falling clock edge instead of rising. */70# define DPI_HSYNC_NEGATE		BIT(6)71# define DPI_VSYNC_NEGATE		BIT(5)72# define DPI_OUTPUT_ENABLE_NEGATE	BIT(4)73 74/* Disables the signal */75# define DPI_HSYNC_DISABLE		BIT(3)76# define DPI_VSYNC_DISABLE		BIT(2)77# define DPI_OUTPUT_ENABLE_DISABLE	BIT(1)78 79/* Power gate to the device, full reset at 0 -> 1 transition */80# define DPI_ENABLE			BIT(0)81 82/* All other registers besides DPI_C return the ID */83#define DPI_ID			0x0484# define DPI_ID_VALUE		0x0064706985 86/* General DPI hardware state. */87struct vc4_dpi {88	struct vc4_encoder encoder;89 90	struct platform_device *pdev;91 92	void __iomem *regs;93 94	struct clk *pixel_clock;95	struct clk *core_clock;96 97	struct debugfs_regset32 regset;98};99 100#define to_vc4_dpi(_encoder)						\101	container_of_const(_encoder, struct vc4_dpi, encoder.base)102 103#define DPI_READ(offset)								\104	({										\105		kunit_fail_current_test("Accessing a register in a unit test!\n");	\106		readl(dpi->regs + (offset));						\107	})108 109#define DPI_WRITE(offset, val)								\110	do {										\111		kunit_fail_current_test("Accessing a register in a unit test!\n");	\112		writel(val, dpi->regs + (offset));					\113	} while (0)114 115static const struct debugfs_reg32 dpi_regs[] = {116	VC4_REG32(DPI_C),117	VC4_REG32(DPI_ID),118};119 120static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)121{122	struct drm_device *dev = encoder->dev;123	struct vc4_dpi *dpi = to_vc4_dpi(encoder);124	int idx;125 126	if (!drm_dev_enter(dev, &idx))127		return;128 129	clk_disable_unprepare(dpi->pixel_clock);130 131	drm_dev_exit(idx);132}133 134static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)135{136	struct drm_device *dev = encoder->dev;137	struct drm_display_mode *mode = &encoder->crtc->mode;138	struct vc4_dpi *dpi = to_vc4_dpi(encoder);139	struct drm_connector_list_iter conn_iter;140	struct drm_connector *connector = NULL, *connector_scan;141	u32 dpi_c = DPI_ENABLE;142	int idx;143	int ret;144 145	/* Look up the connector attached to DPI so we can get the146	 * bus_format.  Ideally the bridge would tell us the147	 * bus_format we want, but it doesn't yet, so assume that it's148	 * uniform throughout the bridge chain.149	 */150	drm_connector_list_iter_begin(dev, &conn_iter);151	drm_for_each_connector_iter(connector_scan, &conn_iter) {152		if (connector_scan->encoder == encoder) {153			connector = connector_scan;154			break;155		}156	}157	drm_connector_list_iter_end(&conn_iter);158 159	/* Default to 18bit if no connector or format found. */160	dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1, DPI_FORMAT);161 162	if (connector) {163		if (connector->display_info.num_bus_formats) {164			u32 bus_format = connector->display_info.bus_formats[0];165 166			dpi_c &= ~DPI_FORMAT_MASK;167 168			switch (bus_format) {169			case MEDIA_BUS_FMT_RGB888_1X24:170				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,171						       DPI_FORMAT);172				break;173			case MEDIA_BUS_FMT_BGR888_1X24:174				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,175						       DPI_FORMAT);176				dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR,177						       DPI_ORDER);178				break;179			case MEDIA_BUS_FMT_BGR666_1X24_CPADHI:180				dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);181				fallthrough;182			case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:183				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,184						       DPI_FORMAT);185				break;186			case MEDIA_BUS_FMT_BGR666_1X18:187				dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);188				fallthrough;189			case MEDIA_BUS_FMT_RGB666_1X18:190				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,191						       DPI_FORMAT);192				break;193			case MEDIA_BUS_FMT_RGB565_1X16:194				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_1,195						       DPI_FORMAT);196				break;197			case MEDIA_BUS_FMT_RGB565_1X24_CPADHI:198				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_2,199						       DPI_FORMAT);200				break;201			default:202				drm_err(dev, "Unknown media bus format %d\n",203					bus_format);204				break;205			}206		}207 208		if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)209			dpi_c |= DPI_PIXEL_CLK_INVERT;210 211		if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW)212			dpi_c |= DPI_OUTPUT_ENABLE_INVERT;213	}214 215	if (mode->flags & DRM_MODE_FLAG_CSYNC) {216		if (mode->flags & DRM_MODE_FLAG_NCSYNC)217			dpi_c |= DPI_OUTPUT_ENABLE_INVERT;218	} else {219		dpi_c |= DPI_OUTPUT_ENABLE_MODE;220 221		if (mode->flags & DRM_MODE_FLAG_NHSYNC)222			dpi_c |= DPI_HSYNC_INVERT;223		else if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))224			dpi_c |= DPI_HSYNC_DISABLE;225 226		if (mode->flags & DRM_MODE_FLAG_NVSYNC)227			dpi_c |= DPI_VSYNC_INVERT;228		else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))229			dpi_c |= DPI_VSYNC_DISABLE;230	}231 232	if (!drm_dev_enter(dev, &idx))233		return;234 235	DPI_WRITE(DPI_C, dpi_c);236 237	ret = clk_set_rate(dpi->pixel_clock, mode->clock * 1000);238	if (ret)239		drm_err(dev, "Failed to set clock rate: %d\n", ret);240 241	ret = clk_prepare_enable(dpi->pixel_clock);242	if (ret)243		drm_err(dev, "Failed to set clock rate: %d\n", ret);244 245	drm_dev_exit(idx);246}247 248static enum drm_mode_status vc4_dpi_encoder_mode_valid(struct drm_encoder *encoder,249						       const struct drm_display_mode *mode)250{251	if (mode->flags & DRM_MODE_FLAG_INTERLACE)252		return MODE_NO_INTERLACE;253 254	return MODE_OK;255}256 257static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = {258	.disable = vc4_dpi_encoder_disable,259	.enable = vc4_dpi_encoder_enable,260	.mode_valid = vc4_dpi_encoder_mode_valid,261};262 263static int vc4_dpi_late_register(struct drm_encoder *encoder)264{265	struct drm_device *drm = encoder->dev;266	struct vc4_dpi *dpi = to_vc4_dpi(encoder);267 268	vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);269 270	return 0;271}272 273static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {274	.late_register = vc4_dpi_late_register,275};276 277static const struct of_device_id vc4_dpi_dt_match[] = {278	{ .compatible = "brcm,bcm2835-dpi", .data = NULL },279	{}280};281 282/* Sets up the next link in the display chain, whether it's a panel or283 * a bridge.284 */285static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)286{287	struct drm_device *drm = dpi->encoder.base.dev;288	struct device *dev = &dpi->pdev->dev;289	struct drm_bridge *bridge;290 291	bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);292	if (IS_ERR(bridge)) {293		/* If nothing was connected in the DT, that's not an294		 * error.295		 */296		if (PTR_ERR(bridge) == -ENODEV)297			return 0;298		else299			return PTR_ERR(bridge);300	}301 302	return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0);303}304 305static void vc4_dpi_disable_clock(void *ptr)306{307	struct vc4_dpi *dpi = ptr;308 309	clk_disable_unprepare(dpi->core_clock);310}311 312static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)313{314	struct platform_device *pdev = to_platform_device(dev);315	struct drm_device *drm = dev_get_drvdata(master);316	struct vc4_dpi *dpi;317	int ret;318 319	dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL);320	if (!dpi)321		return -ENOMEM;322 323	dpi->encoder.type = VC4_ENCODER_TYPE_DPI;324	dpi->pdev = pdev;325	dpi->regs = vc4_ioremap_regs(pdev, 0);326	if (IS_ERR(dpi->regs))327		return PTR_ERR(dpi->regs);328	dpi->regset.base = dpi->regs;329	dpi->regset.regs = dpi_regs;330	dpi->regset.nregs = ARRAY_SIZE(dpi_regs);331 332	if (DPI_READ(DPI_ID) != DPI_ID_VALUE) {333		dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n",334			DPI_READ(DPI_ID), DPI_ID_VALUE);335		return -ENODEV;336	}337 338	dpi->core_clock = devm_clk_get(dev, "core");339	if (IS_ERR(dpi->core_clock)) {340		ret = PTR_ERR(dpi->core_clock);341		if (ret != -EPROBE_DEFER)342			drm_err(drm, "Failed to get core clock: %d\n", ret);343		return ret;344	}345 346	dpi->pixel_clock = devm_clk_get(dev, "pixel");347	if (IS_ERR(dpi->pixel_clock)) {348		ret = PTR_ERR(dpi->pixel_clock);349		if (ret != -EPROBE_DEFER)350			drm_err(drm, "Failed to get pixel clock: %d\n", ret);351		return ret;352	}353 354	ret = clk_prepare_enable(dpi->core_clock);355	if (ret) {356		drm_err(drm, "Failed to turn on core clock: %d\n", ret);357		return ret;358	}359 360	ret = devm_add_action_or_reset(dev, vc4_dpi_disable_clock, dpi);361	if (ret)362		return ret;363 364	ret = drmm_encoder_init(drm, &dpi->encoder.base,365				&vc4_dpi_encoder_funcs,366				DRM_MODE_ENCODER_DPI,367				NULL);368	if (ret)369		return ret;370 371	drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);372 373	ret = vc4_dpi_init_bridge(dpi);374	if (ret)375		return ret;376 377	dev_set_drvdata(dev, dpi);378 379	return 0;380}381 382static const struct component_ops vc4_dpi_ops = {383	.bind   = vc4_dpi_bind,384};385 386static int vc4_dpi_dev_probe(struct platform_device *pdev)387{388	return component_add(&pdev->dev, &vc4_dpi_ops);389}390 391static void vc4_dpi_dev_remove(struct platform_device *pdev)392{393	component_del(&pdev->dev, &vc4_dpi_ops);394}395 396struct platform_driver vc4_dpi_driver = {397	.probe = vc4_dpi_dev_probe,398	.remove_new = vc4_dpi_dev_remove,399	.driver = {400		.name = "vc4_dpi",401		.of_match_table = vc4_dpi_dt_match,402	},403};404