162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/drivers/video/omap2/dss/display.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation
662306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Some code and ideas taken from drivers/video/omap/ driver
962306a36Sopenharmony_ci * by Imre Deak.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define DSS_SUBSYS_NAME "DISPLAY"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/jiffies.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <video/omapfb_dss.h>
2162306a36Sopenharmony_ci#include "dss.h"
2262306a36Sopenharmony_ci#include "dss_features.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_civoid omapdss_default_get_resolution(struct omap_dss_device *dssdev,
2562306a36Sopenharmony_ci			u16 *xres, u16 *yres)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	*xres = dssdev->panel.timings.x_res;
2862306a36Sopenharmony_ci	*yres = dssdev->panel.timings.y_res;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ciEXPORT_SYMBOL(omapdss_default_get_resolution);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciint omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	switch (dssdev->type) {
3562306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_DPI:
3662306a36Sopenharmony_ci		if (dssdev->phy.dpi.data_lines == 24)
3762306a36Sopenharmony_ci			return 24;
3862306a36Sopenharmony_ci		else
3962306a36Sopenharmony_ci			return 16;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_DBI:
4262306a36Sopenharmony_ci		if (dssdev->ctrl.pixel_size == 24)
4362306a36Sopenharmony_ci			return 24;
4462306a36Sopenharmony_ci		else
4562306a36Sopenharmony_ci			return 16;
4662306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_DSI:
4762306a36Sopenharmony_ci		if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16)
4862306a36Sopenharmony_ci			return 24;
4962306a36Sopenharmony_ci		else
5062306a36Sopenharmony_ci			return 16;
5162306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_VENC:
5262306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_SDI:
5362306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_HDMI:
5462306a36Sopenharmony_ci	case OMAP_DISPLAY_TYPE_DVI:
5562306a36Sopenharmony_ci		return 24;
5662306a36Sopenharmony_ci	default:
5762306a36Sopenharmony_ci		BUG();
5862306a36Sopenharmony_ci		return 0;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL(omapdss_default_get_recommended_bpp);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid omapdss_default_get_timings(struct omap_dss_device *dssdev,
6462306a36Sopenharmony_ci		struct omap_video_timings *timings)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	*timings = dssdev->panel.timings;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ciEXPORT_SYMBOL(omapdss_default_get_timings);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint dss_suspend_all_devices(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct omap_dss_device *dssdev = NULL;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	for_each_dss_dev(dssdev) {
7562306a36Sopenharmony_ci		if (!dssdev->driver)
7662306a36Sopenharmony_ci			continue;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
7962306a36Sopenharmony_ci			dssdev->driver->disable(dssdev);
8062306a36Sopenharmony_ci			dssdev->activate_after_resume = true;
8162306a36Sopenharmony_ci		} else {
8262306a36Sopenharmony_ci			dssdev->activate_after_resume = false;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciint dss_resume_all_devices(void)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct omap_dss_device *dssdev = NULL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	for_each_dss_dev(dssdev) {
9462306a36Sopenharmony_ci		if (!dssdev->driver)
9562306a36Sopenharmony_ci			continue;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		if (dssdev->activate_after_resume) {
9862306a36Sopenharmony_ci			dssdev->driver->enable(dssdev);
9962306a36Sopenharmony_ci			dssdev->activate_after_resume = false;
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_civoid dss_disable_all_devices(void)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct omap_dss_device *dssdev = NULL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	for_each_dss_dev(dssdev) {
11162306a36Sopenharmony_ci		if (!dssdev->driver)
11262306a36Sopenharmony_ci			continue;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
11562306a36Sopenharmony_ci			dssdev->driver->disable(dssdev);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic LIST_HEAD(panel_list);
12062306a36Sopenharmony_cistatic DEFINE_MUTEX(panel_list_mutex);
12162306a36Sopenharmony_cistatic int disp_num_counter;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciint omapdss_register_display(struct omap_dss_device *dssdev)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct omap_dss_driver *drv = dssdev->driver;
12662306a36Sopenharmony_ci	int id;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * Note: this presumes all the displays are either using DT or non-DT,
13062306a36Sopenharmony_ci	 * which normally should be the case. This also presumes that all
13162306a36Sopenharmony_ci	 * displays either have an DT alias, or none has.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (dssdev->dev->of_node) {
13562306a36Sopenharmony_ci		id = of_alias_get_id(dssdev->dev->of_node, "display");
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		if (id < 0)
13862306a36Sopenharmony_ci			id = disp_num_counter++;
13962306a36Sopenharmony_ci	} else {
14062306a36Sopenharmony_ci		id = disp_num_counter++;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Use 'label' property for name, if it exists */
14662306a36Sopenharmony_ci	if (dssdev->dev->of_node)
14762306a36Sopenharmony_ci		of_property_read_string(dssdev->dev->of_node, "label",
14862306a36Sopenharmony_ci			&dssdev->name);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (dssdev->name == NULL)
15162306a36Sopenharmony_ci		dssdev->name = dssdev->alias;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (drv && drv->get_resolution == NULL)
15462306a36Sopenharmony_ci		drv->get_resolution = omapdss_default_get_resolution;
15562306a36Sopenharmony_ci	if (drv && drv->get_recommended_bpp == NULL)
15662306a36Sopenharmony_ci		drv->get_recommended_bpp = omapdss_default_get_recommended_bpp;
15762306a36Sopenharmony_ci	if (drv && drv->get_timings == NULL)
15862306a36Sopenharmony_ci		drv->get_timings = omapdss_default_get_timings;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	mutex_lock(&panel_list_mutex);
16162306a36Sopenharmony_ci	list_add_tail(&dssdev->panel_list, &panel_list);
16262306a36Sopenharmony_ci	mutex_unlock(&panel_list_mutex);
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ciEXPORT_SYMBOL(omapdss_register_display);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_civoid omapdss_unregister_display(struct omap_dss_device *dssdev)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	mutex_lock(&panel_list_mutex);
17062306a36Sopenharmony_ci	list_del(&dssdev->panel_list);
17162306a36Sopenharmony_ci	mutex_unlock(&panel_list_mutex);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ciEXPORT_SYMBOL(omapdss_unregister_display);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistruct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	if (!try_module_get(dssdev->owner))
17862306a36Sopenharmony_ci		return NULL;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (get_device(dssdev->dev) == NULL) {
18162306a36Sopenharmony_ci		module_put(dssdev->owner);
18262306a36Sopenharmony_ci		return NULL;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return dssdev;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dss_get_device);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_civoid omap_dss_put_device(struct omap_dss_device *dssdev)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	put_device(dssdev->dev);
19262306a36Sopenharmony_ci	module_put(dssdev->owner);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dss_put_device);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/*
19762306a36Sopenharmony_ci * ref count of the found device is incremented.
19862306a36Sopenharmony_ci * ref count of from-device is decremented.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistruct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct list_head *l;
20362306a36Sopenharmony_ci	struct omap_dss_device *dssdev;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	mutex_lock(&panel_list_mutex);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (list_empty(&panel_list)) {
20862306a36Sopenharmony_ci		dssdev = NULL;
20962306a36Sopenharmony_ci		goto out;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (from == NULL) {
21362306a36Sopenharmony_ci		dssdev = list_first_entry(&panel_list, struct omap_dss_device,
21462306a36Sopenharmony_ci				panel_list);
21562306a36Sopenharmony_ci		omap_dss_get_device(dssdev);
21662306a36Sopenharmony_ci		goto out;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	omap_dss_put_device(from);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	list_for_each(l, &panel_list) {
22262306a36Sopenharmony_ci		dssdev = list_entry(l, struct omap_dss_device, panel_list);
22362306a36Sopenharmony_ci		if (dssdev == from) {
22462306a36Sopenharmony_ci			if (list_is_last(l, &panel_list)) {
22562306a36Sopenharmony_ci				dssdev = NULL;
22662306a36Sopenharmony_ci				goto out;
22762306a36Sopenharmony_ci			}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci			dssdev = list_entry(l->next, struct omap_dss_device,
23062306a36Sopenharmony_ci					panel_list);
23162306a36Sopenharmony_ci			omap_dss_get_device(dssdev);
23262306a36Sopenharmony_ci			goto out;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	WARN(1, "'from' dssdev not found\n");
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	dssdev = NULL;
23962306a36Sopenharmony_ciout:
24062306a36Sopenharmony_ci	mutex_unlock(&panel_list_mutex);
24162306a36Sopenharmony_ci	return dssdev;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dss_get_next_device);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistruct omap_dss_device *omap_dss_find_device(void *data,
24662306a36Sopenharmony_ci		int (*match)(struct omap_dss_device *dssdev, void *data))
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct omap_dss_device *dssdev = NULL;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
25162306a36Sopenharmony_ci		if (match(dssdev, data))
25262306a36Sopenharmony_ci			return dssdev;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return NULL;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dss_find_device);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_civoid videomode_to_omap_video_timings(const struct videomode *vm,
26062306a36Sopenharmony_ci		struct omap_video_timings *ovt)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	memset(ovt, 0, sizeof(*ovt));
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ovt->pixelclock = vm->pixelclock;
26562306a36Sopenharmony_ci	ovt->x_res = vm->hactive;
26662306a36Sopenharmony_ci	ovt->hbp = vm->hback_porch;
26762306a36Sopenharmony_ci	ovt->hfp = vm->hfront_porch;
26862306a36Sopenharmony_ci	ovt->hsw = vm->hsync_len;
26962306a36Sopenharmony_ci	ovt->y_res = vm->vactive;
27062306a36Sopenharmony_ci	ovt->vbp = vm->vback_porch;
27162306a36Sopenharmony_ci	ovt->vfp = vm->vfront_porch;
27262306a36Sopenharmony_ci	ovt->vsw = vm->vsync_len;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
27562306a36Sopenharmony_ci		OMAPDSS_SIG_ACTIVE_HIGH :
27662306a36Sopenharmony_ci		OMAPDSS_SIG_ACTIVE_LOW;
27762306a36Sopenharmony_ci	ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
27862306a36Sopenharmony_ci		OMAPDSS_SIG_ACTIVE_HIGH :
27962306a36Sopenharmony_ci		OMAPDSS_SIG_ACTIVE_LOW;
28062306a36Sopenharmony_ci	ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ?
28162306a36Sopenharmony_ci		OMAPDSS_SIG_ACTIVE_HIGH :
28262306a36Sopenharmony_ci		OMAPDSS_SIG_ACTIVE_LOW;
28362306a36Sopenharmony_ci	ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ?
28462306a36Sopenharmony_ci		OMAPDSS_DRIVE_SIG_RISING_EDGE :
28562306a36Sopenharmony_ci		OMAPDSS_DRIVE_SIG_FALLING_EDGE;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	ovt->sync_pclk_edge = ovt->data_pclk_edge;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ciEXPORT_SYMBOL(videomode_to_omap_video_timings);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_civoid omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
29262306a36Sopenharmony_ci		struct videomode *vm)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	memset(vm, 0, sizeof(*vm));
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	vm->pixelclock = ovt->pixelclock;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	vm->hactive = ovt->x_res;
29962306a36Sopenharmony_ci	vm->hback_porch = ovt->hbp;
30062306a36Sopenharmony_ci	vm->hfront_porch = ovt->hfp;
30162306a36Sopenharmony_ci	vm->hsync_len = ovt->hsw;
30262306a36Sopenharmony_ci	vm->vactive = ovt->y_res;
30362306a36Sopenharmony_ci	vm->vback_porch = ovt->vbp;
30462306a36Sopenharmony_ci	vm->vfront_porch = ovt->vfp;
30562306a36Sopenharmony_ci	vm->vsync_len = ovt->vsw;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
30862306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
30962306a36Sopenharmony_ci	else
31062306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_HSYNC_LOW;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
31362306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
31462306a36Sopenharmony_ci	else
31562306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_VSYNC_LOW;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH)
31862306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_DE_HIGH;
31962306a36Sopenharmony_ci	else
32062306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_DE_LOW;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
32362306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
32462306a36Sopenharmony_ci	else
32562306a36Sopenharmony_ci		vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ciEXPORT_SYMBOL(omap_video_timings_to_videomode);
328