GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / video / fbdev / mmp / core.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * linux/drivers/video/mmp/common.c
4  * This driver is a common framework for Marvell Display Controller
5  *
6  * Copyright (C) 2012 Marvell Technology Group Ltd.
7  * Authors: Zhou Zhu <zzhu3@marvell.com>
8  */
9
10 #include <linux/slab.h>
11 #include <linux/dma-mapping.h>
12 #include <linux/export.h>
13 #include <linux/module.h>
14 #include <video/mmp_disp.h>
15
16 static struct mmp_overlay *path_get_overlay(struct mmp_path *path,
17                 int overlay_id)
18 {
19         if (path && overlay_id < path->overlay_num)
20                 return &path->overlays[overlay_id];
21         return NULL;
22 }
23
24 static int path_check_status(struct mmp_path *path)
25 {
26         int i;
27         for (i = 0; i < path->overlay_num; i++)
28                 if (path->overlays[i].status)
29                         return 1;
30
31         return 0;
32 }
33
34 /*
35  * Get modelist write pointer of modelist.
36  * It also returns modelist number
37  * this function fetches modelist from phy/panel:
38  *   for HDMI/parallel or dsi to hdmi cases, get from phy
39  *   or get from panel
40  */
41 static int path_get_modelist(struct mmp_path *path,
42                 struct mmp_mode **modelist)
43 {
44         BUG_ON(!path || !modelist);
45
46         if (path->panel && path->panel->get_modelist)
47                 return path->panel->get_modelist(path->panel, modelist);
48
49         return 0;
50 }
51
52 /*
53  * panel list is used to pair panel/path when path/panel registered
54  * path list is used for both buffer driver and platdriver
55  * plat driver do path register/unregister
56  * panel driver do panel register/unregister
57  * buffer driver get registered path
58  */
59 static LIST_HEAD(panel_list);
60 static LIST_HEAD(path_list);
61 static DEFINE_MUTEX(disp_lock);
62
63 /*
64  * mmp_register_panel - register panel to panel_list and connect to path
65  * @p: panel to be registered
66  *
67  * this function provides interface for panel drivers to register panel
68  * to panel_list and connect to path which matchs panel->plat_path_name.
69  * no error returns when no matching path is found as path register after
70  * panel register is permitted.
71  */
72 void mmp_register_panel(struct mmp_panel *panel)
73 {
74         struct mmp_path *path;
75
76         mutex_lock(&disp_lock);
77
78         /* add */
79         list_add_tail(&panel->node, &panel_list);
80
81         /* try to register to path */
82         list_for_each_entry(path, &path_list, node) {
83                 if (!strcmp(panel->plat_path_name, path->name)) {
84                         dev_info(panel->dev, "connect to path %s\n",
85                                 path->name);
86                         path->panel = panel;
87                         break;
88                 }
89         }
90
91         mutex_unlock(&disp_lock);
92 }
93 EXPORT_SYMBOL_GPL(mmp_register_panel);
94
95 /*
96  * mmp_unregister_panel - unregister panel from panel_list and disconnect
97  * @p: panel to be unregistered
98  *
99  * this function provides interface for panel drivers to unregister panel
100  * from panel_list and disconnect from path.
101  */
102 void mmp_unregister_panel(struct mmp_panel *panel)
103 {
104         struct mmp_path *path;
105
106         mutex_lock(&disp_lock);
107         list_del(&panel->node);
108
109         list_for_each_entry(path, &path_list, node) {
110                 if (path->panel && path->panel == panel) {
111                         dev_info(panel->dev, "disconnect from path %s\n",
112                                 path->name);
113                         path->panel = NULL;
114                         break;
115                 }
116         }
117         mutex_unlock(&disp_lock);
118 }
119 EXPORT_SYMBOL_GPL(mmp_unregister_panel);
120
121 /*
122  * mmp_get_path - get path by name
123  * @p: path name
124  *
125  * this function checks path name in path_list and return matching path
126  * return NULL if no matching path
127  */
128 struct mmp_path *mmp_get_path(const char *name)
129 {
130         struct mmp_path *path = NULL, *iter;
131
132         mutex_lock(&disp_lock);
133         list_for_each_entry(iter, &path_list, node) {
134                 if (!strcmp(name, iter->name)) {
135                         path = iter;
136                         break;
137                 }
138         }
139         mutex_unlock(&disp_lock);
140
141         return path;
142 }
143 EXPORT_SYMBOL_GPL(mmp_get_path);
144
145 /*
146  * mmp_register_path - init and register path by path_info
147  * @p: path info provided by display controller
148  *
149  * this function init by path info and register path to path_list
150  * this function also try to connect path with panel by name
151  */
152 struct mmp_path *mmp_register_path(struct mmp_path_info *info)
153 {
154         int i;
155         struct mmp_path *path = NULL;
156         struct mmp_panel *panel;
157
158         path = kzalloc(struct_size(path, overlays, info->overlay_num),
159                        GFP_KERNEL);
160         if (!path)
161                 return NULL;
162
163         /* path set */
164         mutex_init(&path->access_ok);
165         path->dev = info->dev;
166         path->id = info->id;
167         path->name = info->name;
168         path->output_type = info->output_type;
169         path->overlay_num = info->overlay_num;
170         path->plat_data = info->plat_data;
171         path->ops.set_mode = info->set_mode;
172
173         mutex_lock(&disp_lock);
174         /* get panel */
175         list_for_each_entry(panel, &panel_list, node) {
176                 if (!strcmp(info->name, panel->plat_path_name)) {
177                         dev_info(path->dev, "get panel %s\n", panel->name);
178                         path->panel = panel;
179                         break;
180                 }
181         }
182
183         dev_info(path->dev, "register %s, overlay_num %d\n",
184                         path->name, path->overlay_num);
185
186         /* default op set: if already set by driver, never cover it */
187         if (!path->ops.check_status)
188                 path->ops.check_status = path_check_status;
189         if (!path->ops.get_overlay)
190                 path->ops.get_overlay = path_get_overlay;
191         if (!path->ops.get_modelist)
192                 path->ops.get_modelist = path_get_modelist;
193
194         /* step3: init overlays */
195         for (i = 0; i < path->overlay_num; i++) {
196                 path->overlays[i].path = path;
197                 path->overlays[i].id = i;
198                 mutex_init(&path->overlays[i].access_ok);
199                 path->overlays[i].ops = info->overlay_ops;
200         }
201
202         /* add to pathlist */
203         list_add_tail(&path->node, &path_list);
204
205         mutex_unlock(&disp_lock);
206         return path;
207 }
208 EXPORT_SYMBOL_GPL(mmp_register_path);
209
210 /*
211  * mmp_unregister_path - unregister and destroy path
212  * @p: path to be destroyed.
213  *
214  * this function registers path and destroys it.
215  */
216 void mmp_unregister_path(struct mmp_path *path)
217 {
218         int i;
219
220         if (!path)
221                 return;
222
223         mutex_lock(&disp_lock);
224         /* del from pathlist */
225         list_del(&path->node);
226
227         /* deinit overlays */
228         for (i = 0; i < path->overlay_num; i++)
229                 mutex_destroy(&path->overlays[i].access_ok);
230
231         mutex_destroy(&path->access_ok);
232
233         kfree(path);
234         mutex_unlock(&disp_lock);
235 }
236 EXPORT_SYMBOL_GPL(mmp_unregister_path);
237
238 MODULE_AUTHOR("Zhou Zhu <zzhu3@marvell.com>");
239 MODULE_DESCRIPTION("Marvell MMP display framework");
240 MODULE_LICENSE("GPL");