brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · 5ba469b Raw
442 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20144 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>5 *          Fabien Dessenne <fabien.dessenne@st.com>6 *          Vincent Abriou <vincent.abriou@st.com>7 *          for STMicroelectronics.8 */9 10#include <linux/module.h>11#include <linux/io.h>12#include <linux/notifier.h>13#include <linux/of_platform.h>14#include <linux/platform_device.h>15 16#include <drm/drm_modes.h>17#include <drm/drm_print.h>18 19#include "sti_drv.h"20#include "sti_vtg.h"21 22#define VTG_MODE_MASTER         023 24/* registers offset */25#define VTG_MODE            0x000026#define VTG_CLKLN           0x000827#define VTG_HLFLN           0x000C28#define VTG_DRST_AUTOC      0x001029#define VTG_VID_TFO         0x004030#define VTG_VID_TFS         0x004431#define VTG_VID_BFO         0x004832#define VTG_VID_BFS         0x004C33 34#define VTG_HOST_ITS        0x007835#define VTG_HOST_ITS_BCLR   0x007C36#define VTG_HOST_ITM_BCLR   0x008837#define VTG_HOST_ITM_BSET   0x008C38 39#define VTG_H_HD_1          0x00C040#define VTG_TOP_V_VD_1      0x00C441#define VTG_BOT_V_VD_1      0x00C842#define VTG_TOP_V_HD_1      0x00CC43#define VTG_BOT_V_HD_1      0x00D044 45#define VTG_H_HD_2          0x00E046#define VTG_TOP_V_VD_2      0x00E447#define VTG_BOT_V_VD_2      0x00E848#define VTG_TOP_V_HD_2      0x00EC49#define VTG_BOT_V_HD_2      0x00F050 51#define VTG_H_HD_3          0x010052#define VTG_TOP_V_VD_3      0x010453#define VTG_BOT_V_VD_3      0x010854#define VTG_TOP_V_HD_3      0x010C55#define VTG_BOT_V_HD_3      0x011056 57#define VTG_H_HD_4          0x012058#define VTG_TOP_V_VD_4      0x012459#define VTG_BOT_V_VD_4      0x012860#define VTG_TOP_V_HD_4      0x012c61#define VTG_BOT_V_HD_4      0x013062 63#define VTG_IRQ_BOTTOM      BIT(0)64#define VTG_IRQ_TOP         BIT(1)65#define VTG_IRQ_MASK        (VTG_IRQ_TOP | VTG_IRQ_BOTTOM)66 67/* Delay introduced by the HDMI in nb of pixel */68#define HDMI_DELAY          (5)69 70/* Delay introduced by the DVO in nb of pixel */71#define DVO_DELAY           (7)72 73/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */74#define AWG_DELAY_HD        (-9)75#define AWG_DELAY_ED        (-8)76#define AWG_DELAY_SD        (-7)77 78/*79 * STI VTG register offset structure80 *81 *@h_hd:     stores the VTG_H_HD_x     register offset82 *@top_v_vd: stores the VTG_TOP_V_VD_x register offset83 *@bot_v_vd: stores the VTG_BOT_V_VD_x register offset84 *@top_v_hd: stores the VTG_TOP_V_HD_x register offset85 *@bot_v_hd: stores the VTG_BOT_V_HD_x register offset86 */87struct sti_vtg_regs_offs {88	u32 h_hd;89	u32 top_v_vd;90	u32 bot_v_vd;91	u32 top_v_hd;92	u32 bot_v_hd;93};94 95#define VTG_MAX_SYNC_OUTPUT 496static const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = {97	{ VTG_H_HD_1,98	  VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 },99	{ VTG_H_HD_2,100	  VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 },101	{ VTG_H_HD_3,102	  VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 },103	{ VTG_H_HD_4,104	  VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 }105};106 107/*108 * STI VTG synchronisation parameters structure109 *110 *@hsync: sample number falling and rising edge111 *@vsync_line_top: vertical top field line number falling and rising edge112 *@vsync_line_bot: vertical bottom field line number falling and rising edge113 *@vsync_off_top: vertical top field sample number rising and falling edge114 *@vsync_off_bot: vertical bottom field sample number rising and falling edge115 */116struct sti_vtg_sync_params {117	u32 hsync;118	u32 vsync_line_top;119	u32 vsync_line_bot;120	u32 vsync_off_top;121	u32 vsync_off_bot;122};123 124/*125 * STI VTG structure126 *127 * @regs: register mapping128 * @sync_params: synchronisation parameters used to generate timings129 * @irq: VTG irq130 * @irq_status: store the IRQ status value131 * @notifier_list: notifier callback132 * @crtc: the CRTC for vblank event133 */134struct sti_vtg {135	void __iomem *regs;136	struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT];137	int irq;138	u32 irq_status;139	struct raw_notifier_head notifier_list;140	struct drm_crtc *crtc;141};142 143struct sti_vtg *of_vtg_find(struct device_node *np)144{145	struct platform_device *pdev;146 147	pdev = of_find_device_by_node(np);148	if (!pdev)149		return NULL;150 151	return (struct sti_vtg *)platform_get_drvdata(pdev);152}153 154static void vtg_reset(struct sti_vtg *vtg)155{156	writel(1, vtg->regs + VTG_DRST_AUTOC);157}158 159static void vtg_set_output_window(void __iomem *regs,160				  const struct drm_display_mode *mode)161{162	u32 video_top_field_start;163	u32 video_top_field_stop;164	u32 video_bottom_field_start;165	u32 video_bottom_field_stop;166	u32 xstart = sti_vtg_get_pixel_number(*mode, 0);167	u32 ystart = sti_vtg_get_line_number(*mode, 0);168	u32 xstop = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);169	u32 ystop = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);170 171	/* Set output window to fit the display mode selected */172	video_top_field_start = (ystart << 16) | xstart;173	video_top_field_stop = (ystop << 16) | xstop;174 175	/* Only progressive supported for now */176	video_bottom_field_start = video_top_field_start;177	video_bottom_field_stop = video_top_field_stop;178 179	writel(video_top_field_start, regs + VTG_VID_TFO);180	writel(video_top_field_stop, regs + VTG_VID_TFS);181	writel(video_bottom_field_start, regs + VTG_VID_BFO);182	writel(video_bottom_field_stop, regs + VTG_VID_BFS);183}184 185static void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync,186				    int delay,187				    const struct drm_display_mode *mode)188{189	long clocksperline, start, stop;190	u32 risesync_top, fallsync_top;191	u32 risesync_offs_top, fallsync_offs_top;192 193	clocksperline = mode->htotal;194 195	/* Get the hsync position */196	start = 0;197	stop = mode->hsync_end - mode->hsync_start;198 199	start += delay;200	stop  += delay;201 202	if (start < 0)203		start += clocksperline;204	else if (start >= clocksperline)205		start -= clocksperline;206 207	if (stop < 0)208		stop += clocksperline;209	else if (stop >= clocksperline)210		stop -= clocksperline;211 212	sync->hsync = (stop << 16) | start;213 214	/* Get the vsync position */215	if (delay >= 0) {216		risesync_top = 1;217		fallsync_top = risesync_top;218		fallsync_top += mode->vsync_end - mode->vsync_start;219 220		fallsync_offs_top = (u32)delay;221		risesync_offs_top = (u32)delay;222	} else {223		risesync_top = mode->vtotal;224		fallsync_top = mode->vsync_end - mode->vsync_start;225 226		fallsync_offs_top = clocksperline + delay;227		risesync_offs_top = clocksperline + delay;228	}229 230	sync->vsync_line_top = (fallsync_top << 16) | risesync_top;231	sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top;232 233	/* Only progressive supported for now */234	sync->vsync_line_bot = sync->vsync_line_top;235	sync->vsync_off_bot = sync->vsync_off_top;236}237 238static void vtg_set_mode(struct sti_vtg *vtg,239			 int type,240			 struct sti_vtg_sync_params *sync,241			 const struct drm_display_mode *mode)242{243	unsigned int i;244 245	/* Set the number of clock cycles per line */246	writel(mode->htotal, vtg->regs + VTG_CLKLN);247 248	/* Set Half Line Per Field (only progressive supported for now) */249	writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN);250 251	/* Program output window */252	vtg_set_output_window(vtg->regs, mode);253 254	/* Set hsync and vsync position for HDMI */255	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode);256 257	/* Set hsync and vsync position for HD DCS */258	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode);259 260	/* Set hsync and vsync position for HDF */261	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode);262 263	/* Set hsync and vsync position for DVO */264	vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode);265 266	/* Progam the syncs outputs */267	for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) {268		writel(sync[i].hsync,269		       vtg->regs + vtg_regs_offs[i].h_hd);270		writel(sync[i].vsync_line_top,271		       vtg->regs + vtg_regs_offs[i].top_v_vd);272		writel(sync[i].vsync_line_bot,273		       vtg->regs + vtg_regs_offs[i].bot_v_vd);274		writel(sync[i].vsync_off_top,275		       vtg->regs + vtg_regs_offs[i].top_v_hd);276		writel(sync[i].vsync_off_bot,277		       vtg->regs + vtg_regs_offs[i].bot_v_hd);278	}279 280	/* mode */281	writel(type, vtg->regs + VTG_MODE);282}283 284static void vtg_enable_irq(struct sti_vtg *vtg)285{286	/* clear interrupt status and mask */287	writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR);288	writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR);289	writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET);290}291 292void sti_vtg_set_config(struct sti_vtg *vtg,293		const struct drm_display_mode *mode)294{295	/* write configuration */296	vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode);297 298	vtg_reset(vtg);299 300	vtg_enable_irq(vtg);301}302 303/**304 * sti_vtg_get_line_number305 *306 * @mode: display mode to be used307 * @y:    line308 *309 * Return the line number according to the display mode taking310 * into account the Sync and Back Porch information.311 * Video frame line numbers start at 1, y starts at 0.312 * In interlaced modes the start line is the field line number of the odd313 * field, but y is still defined as a progressive frame.314 */315u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y)316{317	u32 start_line = mode.vtotal - mode.vsync_start + 1;318 319	if (mode.flags & DRM_MODE_FLAG_INTERLACE)320		start_line *= 2;321 322	return start_line + y;323}324 325/**326 * sti_vtg_get_pixel_number327 *328 * @mode: display mode to be used329 * @x:    row330 *331 * Return the pixel number according to the display mode taking332 * into account the Sync and Back Porch information.333 * Pixels are counted from 0.334 */335u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x)336{337	return mode.htotal - mode.hsync_start + x;338}339 340int sti_vtg_register_client(struct sti_vtg *vtg, struct notifier_block *nb,341			    struct drm_crtc *crtc)342{343	vtg->crtc = crtc;344	return raw_notifier_chain_register(&vtg->notifier_list, nb);345}346 347int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb)348{349	return raw_notifier_chain_unregister(&vtg->notifier_list, nb);350}351 352static irqreturn_t vtg_irq_thread(int irq, void *arg)353{354	struct sti_vtg *vtg = arg;355	u32 event;356 357	event = (vtg->irq_status & VTG_IRQ_TOP) ?358		VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT;359 360	raw_notifier_call_chain(&vtg->notifier_list, event, vtg->crtc);361 362	return IRQ_HANDLED;363}364 365static irqreturn_t vtg_irq(int irq, void *arg)366{367	struct sti_vtg *vtg = arg;368 369	vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS);370 371	writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR);372 373	/* force sync bus write */374	readl(vtg->regs + VTG_HOST_ITS);375 376	return IRQ_WAKE_THREAD;377}378 379static int vtg_probe(struct platform_device *pdev)380{381	struct device *dev = &pdev->dev;382	struct sti_vtg *vtg;383	struct resource *res;384	int ret;385 386	vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL);387	if (!vtg)388		return -ENOMEM;389 390	/* Get Memory ressources */391	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);392	if (!res) {393		DRM_ERROR("Get memory resource failed\n");394		return -ENOMEM;395	}396	vtg->regs = devm_ioremap(dev, res->start, resource_size(res));397	if (!vtg->regs) {398		DRM_ERROR("failed to remap I/O memory\n");399		return -ENOMEM;400	}401 402	vtg->irq = platform_get_irq(pdev, 0);403	if (vtg->irq < 0) {404		DRM_ERROR("Failed to get VTG interrupt\n");405		return vtg->irq;406	}407 408	RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list);409 410	ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq,411					vtg_irq_thread, IRQF_ONESHOT,412					dev_name(dev), vtg);413	if (ret < 0) {414		DRM_ERROR("Failed to register VTG interrupt\n");415		return ret;416	}417 418	platform_set_drvdata(pdev, vtg);419 420	DRM_INFO("%s %s\n", __func__, dev_name(dev));421 422	return 0;423}424 425static const struct of_device_id vtg_of_match[] = {426	{ .compatible = "st,vtg", },427	{ /* sentinel */ }428};429MODULE_DEVICE_TABLE(of, vtg_of_match);430 431struct platform_driver sti_vtg_driver = {432	.driver = {433		.name = "sti-vtg",434		.of_match_table = vtg_of_match,435	},436	.probe	= vtg_probe,437};438 439MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");440MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");441MODULE_LICENSE("GPL");442