xref: /linux/drivers/video/fbdev/mmp/core.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
259393bb9SZhou Zhu /*
359393bb9SZhou Zhu  * linux/drivers/video/mmp/common.c
459393bb9SZhou Zhu  * This driver is a common framework for Marvell Display Controller
559393bb9SZhou Zhu  *
659393bb9SZhou Zhu  * Copyright (C) 2012 Marvell Technology Group Ltd.
759393bb9SZhou Zhu  * Authors: Zhou Zhu <zzhu3@marvell.com>
859393bb9SZhou Zhu  */
959393bb9SZhou Zhu 
1059393bb9SZhou Zhu #include <linux/slab.h>
1159393bb9SZhou Zhu #include <linux/dma-mapping.h>
1259393bb9SZhou Zhu #include <linux/export.h>
13c1530ac5SArnd Bergmann #include <linux/module.h>
1459393bb9SZhou Zhu #include <video/mmp_disp.h>
1559393bb9SZhou Zhu 
1659393bb9SZhou Zhu static struct mmp_overlay *path_get_overlay(struct mmp_path *path,
1759393bb9SZhou Zhu 		int overlay_id)
1859393bb9SZhou Zhu {
1959393bb9SZhou Zhu 	if (path && overlay_id < path->overlay_num)
2059393bb9SZhou Zhu 		return &path->overlays[overlay_id];
216fc19c40SDan Carpenter 	return NULL;
2259393bb9SZhou Zhu }
2359393bb9SZhou Zhu 
2459393bb9SZhou Zhu static int path_check_status(struct mmp_path *path)
2559393bb9SZhou Zhu {
2659393bb9SZhou Zhu 	int i;
2759393bb9SZhou Zhu 	for (i = 0; i < path->overlay_num; i++)
2859393bb9SZhou Zhu 		if (path->overlays[i].status)
2959393bb9SZhou Zhu 			return 1;
3059393bb9SZhou Zhu 
3159393bb9SZhou Zhu 	return 0;
3259393bb9SZhou Zhu }
3359393bb9SZhou Zhu 
3459393bb9SZhou Zhu /*
3559393bb9SZhou Zhu  * Get modelist write pointer of modelist.
3659393bb9SZhou Zhu  * It also returns modelist number
3759393bb9SZhou Zhu  * this function fetches modelist from phy/panel:
3859393bb9SZhou Zhu  *   for HDMI/parallel or dsi to hdmi cases, get from phy
3959393bb9SZhou Zhu  *   or get from panel
4059393bb9SZhou Zhu  */
4159393bb9SZhou Zhu static int path_get_modelist(struct mmp_path *path,
4259393bb9SZhou Zhu 		struct mmp_mode **modelist)
4359393bb9SZhou Zhu {
4459393bb9SZhou Zhu 	BUG_ON(!path || !modelist);
4559393bb9SZhou Zhu 
4659393bb9SZhou Zhu 	if (path->panel && path->panel->get_modelist)
4759393bb9SZhou Zhu 		return path->panel->get_modelist(path->panel, modelist);
4859393bb9SZhou Zhu 
4959393bb9SZhou Zhu 	return 0;
5059393bb9SZhou Zhu }
5159393bb9SZhou Zhu 
5259393bb9SZhou Zhu /*
5359393bb9SZhou Zhu  * panel list is used to pair panel/path when path/panel registered
5459393bb9SZhou Zhu  * path list is used for both buffer driver and platdriver
5559393bb9SZhou Zhu  * plat driver do path register/unregister
5659393bb9SZhou Zhu  * panel driver do panel register/unregister
5759393bb9SZhou Zhu  * buffer driver get registered path
5859393bb9SZhou Zhu  */
5959393bb9SZhou Zhu static LIST_HEAD(panel_list);
6059393bb9SZhou Zhu static LIST_HEAD(path_list);
6159393bb9SZhou Zhu static DEFINE_MUTEX(disp_lock);
6259393bb9SZhou Zhu 
6359393bb9SZhou Zhu /*
6459393bb9SZhou Zhu  * mmp_register_panel - register panel to panel_list and connect to path
6559393bb9SZhou Zhu  * @p: panel to be registered
6659393bb9SZhou Zhu  *
6759393bb9SZhou Zhu  * this function provides interface for panel drivers to register panel
6859393bb9SZhou Zhu  * to panel_list and connect to path which matchs panel->plat_path_name.
6959393bb9SZhou Zhu  * no error returns when no matching path is found as path register after
7059393bb9SZhou Zhu  * panel register is permitted.
7159393bb9SZhou Zhu  */
7259393bb9SZhou Zhu void mmp_register_panel(struct mmp_panel *panel)
7359393bb9SZhou Zhu {
7459393bb9SZhou Zhu 	struct mmp_path *path;
7559393bb9SZhou Zhu 
7659393bb9SZhou Zhu 	mutex_lock(&disp_lock);
7759393bb9SZhou Zhu 
7859393bb9SZhou Zhu 	/* add */
7959393bb9SZhou Zhu 	list_add_tail(&panel->node, &panel_list);
8059393bb9SZhou Zhu 
8159393bb9SZhou Zhu 	/* try to register to path */
8259393bb9SZhou Zhu 	list_for_each_entry(path, &path_list, node) {
8359393bb9SZhou Zhu 		if (!strcmp(panel->plat_path_name, path->name)) {
8459393bb9SZhou Zhu 			dev_info(panel->dev, "connect to path %s\n",
8559393bb9SZhou Zhu 				path->name);
8659393bb9SZhou Zhu 			path->panel = panel;
8759393bb9SZhou Zhu 			break;
8859393bb9SZhou Zhu 		}
8959393bb9SZhou Zhu 	}
9059393bb9SZhou Zhu 
9159393bb9SZhou Zhu 	mutex_unlock(&disp_lock);
9259393bb9SZhou Zhu }
9359393bb9SZhou Zhu EXPORT_SYMBOL_GPL(mmp_register_panel);
9459393bb9SZhou Zhu 
9559393bb9SZhou Zhu /*
9659393bb9SZhou Zhu  * mmp_unregister_panel - unregister panel from panel_list and disconnect
9759393bb9SZhou Zhu  * @p: panel to be unregistered
9859393bb9SZhou Zhu  *
9959393bb9SZhou Zhu  * this function provides interface for panel drivers to unregister panel
10059393bb9SZhou Zhu  * from panel_list and disconnect from path.
10159393bb9SZhou Zhu  */
10259393bb9SZhou Zhu void mmp_unregister_panel(struct mmp_panel *panel)
10359393bb9SZhou Zhu {
10459393bb9SZhou Zhu 	struct mmp_path *path;
10559393bb9SZhou Zhu 
10659393bb9SZhou Zhu 	mutex_lock(&disp_lock);
10759393bb9SZhou Zhu 	list_del(&panel->node);
10859393bb9SZhou Zhu 
10959393bb9SZhou Zhu 	list_for_each_entry(path, &path_list, node) {
11059393bb9SZhou Zhu 		if (path->panel && path->panel == panel) {
11159393bb9SZhou Zhu 			dev_info(panel->dev, "disconnect from path %s\n",
11259393bb9SZhou Zhu 				path->name);
11359393bb9SZhou Zhu 			path->panel = NULL;
11459393bb9SZhou Zhu 			break;
11559393bb9SZhou Zhu 		}
11659393bb9SZhou Zhu 	}
11759393bb9SZhou Zhu 	mutex_unlock(&disp_lock);
11859393bb9SZhou Zhu }
11959393bb9SZhou Zhu EXPORT_SYMBOL_GPL(mmp_unregister_panel);
12059393bb9SZhou Zhu 
12159393bb9SZhou Zhu /*
12259393bb9SZhou Zhu  * mmp_get_path - get path by name
12359393bb9SZhou Zhu  * @p: path name
12459393bb9SZhou Zhu  *
12559393bb9SZhou Zhu  * this function checks path name in path_list and return matching path
12659393bb9SZhou Zhu  * return NULL if no matching path
12759393bb9SZhou Zhu  */
12859393bb9SZhou Zhu struct mmp_path *mmp_get_path(const char *name)
12959393bb9SZhou Zhu {
13059393bb9SZhou Zhu 	struct mmp_path *path;
13159393bb9SZhou Zhu 	int found = 0;
13259393bb9SZhou Zhu 
13359393bb9SZhou Zhu 	mutex_lock(&disp_lock);
13459393bb9SZhou Zhu 	list_for_each_entry(path, &path_list, node) {
13559393bb9SZhou Zhu 		if (!strcmp(name, path->name)) {
13659393bb9SZhou Zhu 			found = 1;
13759393bb9SZhou Zhu 			break;
13859393bb9SZhou Zhu 		}
13959393bb9SZhou Zhu 	}
14059393bb9SZhou Zhu 	mutex_unlock(&disp_lock);
14159393bb9SZhou Zhu 
14259393bb9SZhou Zhu 	return found ? path : NULL;
14359393bb9SZhou Zhu }
14459393bb9SZhou Zhu EXPORT_SYMBOL_GPL(mmp_get_path);
14559393bb9SZhou Zhu 
14659393bb9SZhou Zhu /*
14759393bb9SZhou Zhu  * mmp_register_path - init and register path by path_info
14859393bb9SZhou Zhu  * @p: path info provided by display controller
14959393bb9SZhou Zhu  *
15059393bb9SZhou Zhu  * this function init by path info and register path to path_list
15159393bb9SZhou Zhu  * this function also try to connect path with panel by name
15259393bb9SZhou Zhu  */
15359393bb9SZhou Zhu struct mmp_path *mmp_register_path(struct mmp_path_info *info)
15459393bb9SZhou Zhu {
15559393bb9SZhou Zhu 	int i;
15659393bb9SZhou Zhu 	struct mmp_path *path = NULL;
15759393bb9SZhou Zhu 	struct mmp_panel *panel;
15859393bb9SZhou Zhu 
159*06b1f4b9SGustavo A. R. Silva 	path = kzalloc(struct_size(path, overlays, info->overlay_num),
160*06b1f4b9SGustavo A. R. Silva 		       GFP_KERNEL);
16159393bb9SZhou Zhu 	if (!path)
1624ac8bd61SDan Carpenter 		return NULL;
16359393bb9SZhou Zhu 
16459393bb9SZhou Zhu 	/* path set */
16559393bb9SZhou Zhu 	mutex_init(&path->access_ok);
16659393bb9SZhou Zhu 	path->dev = info->dev;
16759393bb9SZhou Zhu 	path->id = info->id;
16859393bb9SZhou Zhu 	path->name = info->name;
16959393bb9SZhou Zhu 	path->output_type = info->output_type;
17059393bb9SZhou Zhu 	path->overlay_num = info->overlay_num;
17159393bb9SZhou Zhu 	path->plat_data = info->plat_data;
17259393bb9SZhou Zhu 	path->ops.set_mode = info->set_mode;
17359393bb9SZhou Zhu 
17459393bb9SZhou Zhu 	mutex_lock(&disp_lock);
17559393bb9SZhou Zhu 	/* get panel */
17659393bb9SZhou Zhu 	list_for_each_entry(panel, &panel_list, node) {
17759393bb9SZhou Zhu 		if (!strcmp(info->name, panel->plat_path_name)) {
17859393bb9SZhou Zhu 			dev_info(path->dev, "get panel %s\n", panel->name);
17959393bb9SZhou Zhu 			path->panel = panel;
18059393bb9SZhou Zhu 			break;
18159393bb9SZhou Zhu 		}
18259393bb9SZhou Zhu 	}
18359393bb9SZhou Zhu 
18459393bb9SZhou Zhu 	dev_info(path->dev, "register %s, overlay_num %d\n",
18559393bb9SZhou Zhu 			path->name, path->overlay_num);
18659393bb9SZhou Zhu 
18759393bb9SZhou Zhu 	/* default op set: if already set by driver, never cover it */
18859393bb9SZhou Zhu 	if (!path->ops.check_status)
18959393bb9SZhou Zhu 		path->ops.check_status = path_check_status;
19059393bb9SZhou Zhu 	if (!path->ops.get_overlay)
19159393bb9SZhou Zhu 		path->ops.get_overlay = path_get_overlay;
19259393bb9SZhou Zhu 	if (!path->ops.get_modelist)
19359393bb9SZhou Zhu 		path->ops.get_modelist = path_get_modelist;
19459393bb9SZhou Zhu 
19559393bb9SZhou Zhu 	/* step3: init overlays */
19659393bb9SZhou Zhu 	for (i = 0; i < path->overlay_num; i++) {
19759393bb9SZhou Zhu 		path->overlays[i].path = path;
19859393bb9SZhou Zhu 		path->overlays[i].id = i;
19959393bb9SZhou Zhu 		mutex_init(&path->overlays[i].access_ok);
20059393bb9SZhou Zhu 		path->overlays[i].ops = info->overlay_ops;
20159393bb9SZhou Zhu 	}
20259393bb9SZhou Zhu 
20359393bb9SZhou Zhu 	/* add to pathlist */
20459393bb9SZhou Zhu 	list_add_tail(&path->node, &path_list);
20559393bb9SZhou Zhu 
20659393bb9SZhou Zhu 	mutex_unlock(&disp_lock);
20759393bb9SZhou Zhu 	return path;
20859393bb9SZhou Zhu }
20959393bb9SZhou Zhu EXPORT_SYMBOL_GPL(mmp_register_path);
21059393bb9SZhou Zhu 
21159393bb9SZhou Zhu /*
2129e09d00fSMarkus Elfring  * mmp_unregister_path - unregister and destroy path
21323c12eacSMarkus Elfring  * @p: path to be destroyed.
21459393bb9SZhou Zhu  *
2159e09d00fSMarkus Elfring  * this function registers path and destroys it.
21659393bb9SZhou Zhu  */
21759393bb9SZhou Zhu void mmp_unregister_path(struct mmp_path *path)
21859393bb9SZhou Zhu {
21959393bb9SZhou Zhu 	int i;
22059393bb9SZhou Zhu 
22159393bb9SZhou Zhu 	if (!path)
22259393bb9SZhou Zhu 		return;
22359393bb9SZhou Zhu 
22459393bb9SZhou Zhu 	mutex_lock(&disp_lock);
22559393bb9SZhou Zhu 	/* del from pathlist */
22659393bb9SZhou Zhu 	list_del(&path->node);
22759393bb9SZhou Zhu 
22859393bb9SZhou Zhu 	/* deinit overlays */
22959393bb9SZhou Zhu 	for (i = 0; i < path->overlay_num; i++)
23059393bb9SZhou Zhu 		mutex_destroy(&path->overlays[i].access_ok);
23159393bb9SZhou Zhu 
23259393bb9SZhou Zhu 	mutex_destroy(&path->access_ok);
23359393bb9SZhou Zhu 
23459393bb9SZhou Zhu 	kfree(path);
23559393bb9SZhou Zhu 	mutex_unlock(&disp_lock);
23659393bb9SZhou Zhu }
23759393bb9SZhou Zhu EXPORT_SYMBOL_GPL(mmp_unregister_path);
238c1530ac5SArnd Bergmann 
239c1530ac5SArnd Bergmann MODULE_AUTHOR("Zhou Zhu <zzhu3@marvell.com>");
240c1530ac5SArnd Bergmann MODULE_DESCRIPTION("Marvell MMP display framework");
241c1530ac5SArnd Bergmann MODULE_LICENSE("GPL");
242