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(new: &panel->node, head: &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(lock: &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(entry: &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(lock: &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(lock: &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(new: &path->node, head: &path_list); |
204 | |
205 | mutex_unlock(lock: &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(entry: &path->node); |
226 | |
227 | /* deinit overlays */ |
228 | for (i = 0; i < path->overlay_num; i++) |
229 | mutex_destroy(lock: &path->overlays[i].access_ok); |
230 | |
231 | mutex_destroy(lock: &path->access_ok); |
232 | |
233 | kfree(objp: path); |
234 | mutex_unlock(lock: &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" ); |
241 | |