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