162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/drivers/video/omap2/dss/manager.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 "MANAGER"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/jiffies.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <video/omapfb_dss.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "dss.h"
2362306a36Sopenharmony_ci#include "dss_features.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int num_managers;
2662306a36Sopenharmony_cistatic struct omap_overlay_manager *managers;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciint dss_init_overlay_managers(void)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	int i;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	num_managers = dss_feat_get_num_mgrs();
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	managers = kcalloc(num_managers, sizeof(struct omap_overlay_manager),
3562306a36Sopenharmony_ci			   GFP_KERNEL);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	BUG_ON(managers == NULL);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	for (i = 0; i < num_managers; ++i) {
4062306a36Sopenharmony_ci		struct omap_overlay_manager *mgr = &managers[i];
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		switch (i) {
4362306a36Sopenharmony_ci		case 0:
4462306a36Sopenharmony_ci			mgr->name = "lcd";
4562306a36Sopenharmony_ci			mgr->id = OMAP_DSS_CHANNEL_LCD;
4662306a36Sopenharmony_ci			break;
4762306a36Sopenharmony_ci		case 1:
4862306a36Sopenharmony_ci			mgr->name = "tv";
4962306a36Sopenharmony_ci			mgr->id = OMAP_DSS_CHANNEL_DIGIT;
5062306a36Sopenharmony_ci			break;
5162306a36Sopenharmony_ci		case 2:
5262306a36Sopenharmony_ci			mgr->name = "lcd2";
5362306a36Sopenharmony_ci			mgr->id = OMAP_DSS_CHANNEL_LCD2;
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci		case 3:
5662306a36Sopenharmony_ci			mgr->name = "lcd3";
5762306a36Sopenharmony_ci			mgr->id = OMAP_DSS_CHANNEL_LCD3;
5862306a36Sopenharmony_ci			break;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		mgr->supported_displays =
6262306a36Sopenharmony_ci			dss_feat_get_supported_displays(mgr->id);
6362306a36Sopenharmony_ci		mgr->supported_outputs =
6462306a36Sopenharmony_ci			dss_feat_get_supported_outputs(mgr->id);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		INIT_LIST_HEAD(&mgr->overlays);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciint dss_init_overlay_managers_sysfs(struct platform_device *pdev)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int i, r;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	for (i = 0; i < num_managers; ++i) {
7762306a36Sopenharmony_ci		struct omap_overlay_manager *mgr = &managers[i];
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		r = dss_manager_kobj_init(mgr, pdev);
8062306a36Sopenharmony_ci		if (r)
8162306a36Sopenharmony_ci			DSSERR("failed to create sysfs file\n");
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid dss_uninit_overlay_managers(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	kfree(managers);
9062306a36Sopenharmony_ci	managers = NULL;
9162306a36Sopenharmony_ci	num_managers = 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_civoid dss_uninit_overlay_managers_sysfs(struct platform_device *pdev)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int i;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	for (i = 0; i < num_managers; ++i) {
9962306a36Sopenharmony_ci		struct omap_overlay_manager *mgr = &managers[i];
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		dss_manager_kobj_uninit(mgr);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciint omap_dss_get_num_overlay_managers(void)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	return num_managers;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	if (num >= num_managers)
11462306a36Sopenharmony_ci		return NULL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return &managers[num];
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dss_get_overlay_manager);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint dss_mgr_simple_check(struct omap_overlay_manager *mgr,
12162306a36Sopenharmony_ci		const struct omap_overlay_manager_info *info)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
12462306a36Sopenharmony_ci		/*
12562306a36Sopenharmony_ci		 * OMAP3 supports only graphics source transparency color key
12662306a36Sopenharmony_ci		 * and alpha blending simultaneously. See TRM 15.4.2.4.2.2
12762306a36Sopenharmony_ci		 * Alpha Mode.
12862306a36Sopenharmony_ci		 */
12962306a36Sopenharmony_ci		if (info->partial_alpha_enabled && info->trans_enabled
13062306a36Sopenharmony_ci			&& info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) {
13162306a36Sopenharmony_ci			DSSERR("check_manager: illegal transparency key\n");
13262306a36Sopenharmony_ci			return -EINVAL;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
14062306a36Sopenharmony_ci		struct omap_overlay_info **overlay_infos)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct omap_overlay *ovl1, *ovl2;
14362306a36Sopenharmony_ci	struct omap_overlay_info *info1, *info2;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	list_for_each_entry(ovl1, &mgr->overlays, list) {
14662306a36Sopenharmony_ci		info1 = overlay_infos[ovl1->id];
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		if (info1 == NULL)
14962306a36Sopenharmony_ci			continue;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		list_for_each_entry(ovl2, &mgr->overlays, list) {
15262306a36Sopenharmony_ci			if (ovl1 == ovl2)
15362306a36Sopenharmony_ci				continue;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci			info2 = overlay_infos[ovl2->id];
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci			if (info2 == NULL)
15862306a36Sopenharmony_ci				continue;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci			if (info1->zorder == info2->zorder) {
16162306a36Sopenharmony_ci				DSSERR("overlays %d and %d have the same "
16262306a36Sopenharmony_ci						"zorder %d\n",
16362306a36Sopenharmony_ci					ovl1->id, ovl2->id, info1->zorder);
16462306a36Sopenharmony_ci				return -EINVAL;
16562306a36Sopenharmony_ci			}
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ciint dss_mgr_check_timings(struct omap_overlay_manager *mgr,
17362306a36Sopenharmony_ci		const struct omap_video_timings *timings)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	if (!dispc_mgr_timings_ok(mgr->id, timings)) {
17662306a36Sopenharmony_ci		DSSERR("check_manager: invalid timings\n");
17762306a36Sopenharmony_ci		return -EINVAL;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
18462306a36Sopenharmony_ci		const struct dss_lcd_mgr_config *config)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct dispc_clock_info cinfo = config->clock_info;
18762306a36Sopenharmony_ci	int dl = config->video_port_width;
18862306a36Sopenharmony_ci	bool stallmode = config->stallmode;
18962306a36Sopenharmony_ci	bool fifohandcheck = config->fifohandcheck;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (cinfo.lck_div < 1 || cinfo.lck_div > 255)
19262306a36Sopenharmony_ci		return -EINVAL;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (cinfo.pck_div < 1 || cinfo.pck_div > 255)
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (dl != 12 && dl != 16 && dl != 18 && dl != 24)
19862306a36Sopenharmony_ci		return -EINVAL;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* fifohandcheck should be used only with stallmode */
20162306a36Sopenharmony_ci	if (!stallmode && fifohandcheck)
20262306a36Sopenharmony_ci		return -EINVAL;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/*
20562306a36Sopenharmony_ci	 * io pad mode can be only checked by using dssdev connected to the
20662306a36Sopenharmony_ci	 * manager. Ignore checking these for now, add checks when manager
20762306a36Sopenharmony_ci	 * is capable of holding information related to the connected interface
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciint dss_mgr_check(struct omap_overlay_manager *mgr,
21462306a36Sopenharmony_ci		struct omap_overlay_manager_info *info,
21562306a36Sopenharmony_ci		const struct omap_video_timings *mgr_timings,
21662306a36Sopenharmony_ci		const struct dss_lcd_mgr_config *lcd_config,
21762306a36Sopenharmony_ci		struct omap_overlay_info **overlay_infos)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct omap_overlay *ovl;
22062306a36Sopenharmony_ci	int r;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
22362306a36Sopenharmony_ci		r = dss_mgr_check_zorder(mgr, overlay_infos);
22462306a36Sopenharmony_ci		if (r)
22562306a36Sopenharmony_ci			return r;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	r = dss_mgr_check_timings(mgr, mgr_timings);
22962306a36Sopenharmony_ci	if (r)
23062306a36Sopenharmony_ci		return r;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	r = dss_mgr_check_lcd_config(mgr, lcd_config);
23362306a36Sopenharmony_ci	if (r)
23462306a36Sopenharmony_ci		return r;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	list_for_each_entry(ovl, &mgr->overlays, list) {
23762306a36Sopenharmony_ci		struct omap_overlay_info *oi;
23862306a36Sopenharmony_ci		int r;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		oi = overlay_infos[ovl->id];
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		if (oi == NULL)
24362306a36Sopenharmony_ci			continue;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		r = dss_ovl_check(ovl, oi, mgr_timings);
24662306a36Sopenharmony_ci		if (r)
24762306a36Sopenharmony_ci			return r;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
252