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