11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2bb5cdf8dSAndrew F. Davis /* 3bb5cdf8dSAndrew F. Davis * OMAP Display Subsystem Base 4bb5cdf8dSAndrew F. Davis * 5*1b409fdaSAlexander A. Klimov * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ 6bb5cdf8dSAndrew F. Davis */ 7bb5cdf8dSAndrew F. Davis 8a99ac0d9STomi Valkeinen #include <linux/kernel.h> 96a7c5a22SLaurent Pinchart #include <linux/list.h> 10a99ac0d9STomi Valkeinen #include <linux/module.h> 116a7c5a22SLaurent Pinchart #include <linux/mutex.h> 121e08c822SPeter Ujfalusi #include <linux/of.h> 131e08c822SPeter Ujfalusi #include <linux/of_graph.h> 1479107f27SLaurent Pinchart #include <linux/platform_device.h> 15d3541ca8SLaurent Pinchart 16d3541ca8SLaurent Pinchart #include "dss.h" 171e08c822SPeter Ujfalusi #include "omapdss.h" 18a99ac0d9STomi Valkeinen 1972877cf3SLaurent Pinchart static struct dss_device *dss_device; 207c299716STomi Valkeinen 2172877cf3SLaurent Pinchart struct dss_device *omapdss_get_dss(void) 227c299716STomi Valkeinen { 2372877cf3SLaurent Pinchart return dss_device; 247c299716STomi Valkeinen } 2572877cf3SLaurent Pinchart EXPORT_SYMBOL(omapdss_get_dss); 267c299716STomi Valkeinen 2772877cf3SLaurent Pinchart void omapdss_set_dss(struct dss_device *dss) 287c299716STomi Valkeinen { 2972877cf3SLaurent Pinchart dss_device = dss; 307c299716STomi Valkeinen } 3172877cf3SLaurent Pinchart EXPORT_SYMBOL(omapdss_set_dss); 327c299716STomi Valkeinen 3350638ae5SLaurent Pinchart struct dispc_device *dispc_get_dispc(struct dss_device *dss) 3450638ae5SLaurent Pinchart { 3550638ae5SLaurent Pinchart return dss->dispc; 3650638ae5SLaurent Pinchart } 3750638ae5SLaurent Pinchart EXPORT_SYMBOL(dispc_get_dispc); 3850638ae5SLaurent Pinchart 39d3541ca8SLaurent Pinchart const struct dispc_ops *dispc_get_ops(struct dss_device *dss) 408a13398cSTomi Valkeinen { 41d3541ca8SLaurent Pinchart return dss->dispc_ops; 428a13398cSTomi Valkeinen } 438a13398cSTomi Valkeinen EXPORT_SYMBOL(dispc_get_ops); 448a13398cSTomi Valkeinen 456a7c5a22SLaurent Pinchart 466a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 476a7c5a22SLaurent Pinchart * OMAP DSS Devices Handling 486a7c5a22SLaurent Pinchart */ 496a7c5a22SLaurent Pinchart 506a7c5a22SLaurent Pinchart static LIST_HEAD(omapdss_devices_list); 516a7c5a22SLaurent Pinchart static DEFINE_MUTEX(omapdss_devices_lock); 526a7c5a22SLaurent Pinchart 536a7c5a22SLaurent Pinchart void omapdss_device_register(struct omap_dss_device *dssdev) 546a7c5a22SLaurent Pinchart { 556a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 566a7c5a22SLaurent Pinchart list_add_tail(&dssdev->list, &omapdss_devices_list); 576a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 586a7c5a22SLaurent Pinchart } 59de57e9dbSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_register); 606a7c5a22SLaurent Pinchart 616a7c5a22SLaurent Pinchart void omapdss_device_unregister(struct omap_dss_device *dssdev) 626a7c5a22SLaurent Pinchart { 636a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 646a7c5a22SLaurent Pinchart list_del(&dssdev->list); 656a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 666a7c5a22SLaurent Pinchart } 67de57e9dbSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_unregister); 686a7c5a22SLaurent Pinchart 699184f8d9SLaurent Pinchart static bool omapdss_device_is_registered(struct device_node *node) 709184f8d9SLaurent Pinchart { 719184f8d9SLaurent Pinchart struct omap_dss_device *dssdev; 729184f8d9SLaurent Pinchart bool found = false; 739184f8d9SLaurent Pinchart 749184f8d9SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 759184f8d9SLaurent Pinchart 769184f8d9SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 779184f8d9SLaurent Pinchart if (dssdev->dev->of_node == node) { 789184f8d9SLaurent Pinchart found = true; 799184f8d9SLaurent Pinchart break; 809184f8d9SLaurent Pinchart } 819184f8d9SLaurent Pinchart } 829184f8d9SLaurent Pinchart 839184f8d9SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 849184f8d9SLaurent Pinchart return found; 859184f8d9SLaurent Pinchart } 869184f8d9SLaurent Pinchart 87c1dfe721SLaurent Pinchart struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev) 88c1dfe721SLaurent Pinchart { 89c1dfe721SLaurent Pinchart if (!try_module_get(dssdev->owner)) 90c1dfe721SLaurent Pinchart return NULL; 91c1dfe721SLaurent Pinchart 92c1dfe721SLaurent Pinchart if (get_device(dssdev->dev) == NULL) { 93c1dfe721SLaurent Pinchart module_put(dssdev->owner); 94c1dfe721SLaurent Pinchart return NULL; 95c1dfe721SLaurent Pinchart } 96c1dfe721SLaurent Pinchart 97c1dfe721SLaurent Pinchart return dssdev; 98c1dfe721SLaurent Pinchart } 99c1dfe721SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_get); 100c1dfe721SLaurent Pinchart 101c1dfe721SLaurent Pinchart void omapdss_device_put(struct omap_dss_device *dssdev) 102c1dfe721SLaurent Pinchart { 103c1dfe721SLaurent Pinchart put_device(dssdev->dev); 104c1dfe721SLaurent Pinchart module_put(dssdev->owner); 105c1dfe721SLaurent Pinchart } 106c1dfe721SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_put); 107c1dfe721SLaurent Pinchart 108ce69aac8SLaurent Pinchart struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node) 109e10bd354SLaurent Pinchart { 110e10bd354SLaurent Pinchart struct omap_dss_device *dssdev; 111e10bd354SLaurent Pinchart 112e10bd354SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 113ce69aac8SLaurent Pinchart if (dssdev->dev->of_node == node) 114c1dfe721SLaurent Pinchart return omapdss_device_get(dssdev); 115e10bd354SLaurent Pinchart } 116e10bd354SLaurent Pinchart 117e10bd354SLaurent Pinchart return NULL; 118e10bd354SLaurent Pinchart } 119e10bd354SLaurent Pinchart 120b9f4d2ebSLaurent Pinchart /* 12119b4200dSLaurent Pinchart * Search for the next output device starting at @from. Release the reference to 12219b4200dSLaurent Pinchart * the @from device, and acquire a reference to the returned device if found. 123b9f4d2ebSLaurent Pinchart */ 12419b4200dSLaurent Pinchart struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from) 125b9f4d2ebSLaurent Pinchart { 126b9f4d2ebSLaurent Pinchart struct omap_dss_device *dssdev; 127b9f4d2ebSLaurent Pinchart struct list_head *list; 128b9f4d2ebSLaurent Pinchart 129b9f4d2ebSLaurent Pinchart mutex_lock(&omapdss_devices_lock); 130b9f4d2ebSLaurent Pinchart 131b9f4d2ebSLaurent Pinchart if (list_empty(&omapdss_devices_list)) { 132b9f4d2ebSLaurent Pinchart dssdev = NULL; 133b9f4d2ebSLaurent Pinchart goto done; 134b9f4d2ebSLaurent Pinchart } 135b9f4d2ebSLaurent Pinchart 136b9f4d2ebSLaurent Pinchart /* 137b9f4d2ebSLaurent Pinchart * Start from the from entry if given or from omapdss_devices_list 138b9f4d2ebSLaurent Pinchart * otherwise. 139b9f4d2ebSLaurent Pinchart */ 140b9f4d2ebSLaurent Pinchart list = from ? &from->list : &omapdss_devices_list; 141b9f4d2ebSLaurent Pinchart 142b9f4d2ebSLaurent Pinchart list_for_each_entry(dssdev, list, list) { 143b9f4d2ebSLaurent Pinchart /* 144b9f4d2ebSLaurent Pinchart * Stop if we reach the omapdss_devices_list, that's the end of 145b9f4d2ebSLaurent Pinchart * the list. 146b9f4d2ebSLaurent Pinchart */ 147b9f4d2ebSLaurent Pinchart if (&dssdev->list == &omapdss_devices_list) { 148b9f4d2ebSLaurent Pinchart dssdev = NULL; 149b9f4d2ebSLaurent Pinchart goto done; 150b9f4d2ebSLaurent Pinchart } 151b9f4d2ebSLaurent Pinchart 152a779618bSLaurent Pinchart if (dssdev->id && (dssdev->next || dssdev->bridge)) 153b9f4d2ebSLaurent Pinchart goto done; 154b9f4d2ebSLaurent Pinchart } 155b9f4d2ebSLaurent Pinchart 156b9f4d2ebSLaurent Pinchart dssdev = NULL; 157b9f4d2ebSLaurent Pinchart 158b9f4d2ebSLaurent Pinchart done: 159b9f4d2ebSLaurent Pinchart if (from) 160c1dfe721SLaurent Pinchart omapdss_device_put(from); 161b9f4d2ebSLaurent Pinchart if (dssdev) 162c1dfe721SLaurent Pinchart omapdss_device_get(dssdev); 163b9f4d2ebSLaurent Pinchart 164b9f4d2ebSLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 165b9f4d2ebSLaurent Pinchart return dssdev; 166b9f4d2ebSLaurent Pinchart } 16719b4200dSLaurent Pinchart EXPORT_SYMBOL(omapdss_device_next_output); 168b9f4d2ebSLaurent Pinchart 169b49a2139SLaurent Pinchart static bool omapdss_device_is_connected(struct omap_dss_device *dssdev) 170b49a2139SLaurent Pinchart { 171df6682b4SLaurent Pinchart return dssdev->dss; 172b49a2139SLaurent Pinchart } 173b49a2139SLaurent Pinchart 174f324b279SLaurent Pinchart int omapdss_device_connect(struct dss_device *dss, 175f324b279SLaurent Pinchart struct omap_dss_device *src, 176ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 177ec727e3fSLaurent Pinchart { 178fb557171SLaurent Pinchart int ret; 179fb557171SLaurent Pinchart 18079107f27SLaurent Pinchart dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n", 18179107f27SLaurent Pinchart src ? dev_name(src->dev) : "NULL", 18279107f27SLaurent Pinchart dst ? dev_name(dst->dev) : "NULL"); 18379107f27SLaurent Pinchart 18479107f27SLaurent Pinchart if (!dst) { 18579107f27SLaurent Pinchart /* 18679107f27SLaurent Pinchart * The destination is NULL when the source is connected to a 187a779618bSLaurent Pinchart * bridge instead of a DSS device. Stop here, we will attach 188a779618bSLaurent Pinchart * the bridge later when we will have a DRM encoder. 18979107f27SLaurent Pinchart */ 190a779618bSLaurent Pinchart return src && src->bridge ? 0 : -EINVAL; 19179107f27SLaurent Pinchart } 1921f507968SLaurent Pinchart 193511afb44SLaurent Pinchart if (omapdss_device_is_connected(dst)) 1941f507968SLaurent Pinchart return -EBUSY; 1951f507968SLaurent Pinchart 196511afb44SLaurent Pinchart dst->dss = dss; 197f324b279SLaurent Pinchart 198db0fefd1SLaurent Pinchart if (dst->ops && dst->ops->connect) { 199511afb44SLaurent Pinchart ret = dst->ops->connect(src, dst); 200f324b279SLaurent Pinchart if (ret < 0) { 201511afb44SLaurent Pinchart dst->dss = NULL; 202fb557171SLaurent Pinchart return ret; 203f324b279SLaurent Pinchart } 204db0fefd1SLaurent Pinchart } 205fb557171SLaurent Pinchart 206fb557171SLaurent Pinchart return 0; 207ec727e3fSLaurent Pinchart } 208ec727e3fSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_connect); 209ec727e3fSLaurent Pinchart 210ec727e3fSLaurent Pinchart void omapdss_device_disconnect(struct omap_dss_device *src, 211ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 212ec727e3fSLaurent Pinchart { 21379107f27SLaurent Pinchart struct dss_device *dss = src ? src->dss : dst->dss; 21479107f27SLaurent Pinchart 21579107f27SLaurent Pinchart dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n", 21679107f27SLaurent Pinchart src ? dev_name(src->dev) : "NULL", 21779107f27SLaurent Pinchart dst ? dev_name(dst->dev) : "NULL"); 21879107f27SLaurent Pinchart 21979107f27SLaurent Pinchart if (!dst) { 220a779618bSLaurent Pinchart WARN_ON(!src->bridge); 22179107f27SLaurent Pinchart return; 22279107f27SLaurent Pinchart } 2231f507968SLaurent Pinchart 224511afb44SLaurent Pinchart if (!dst->id && !omapdss_device_is_connected(dst)) { 2250dbfc396SLaurent Pinchart WARN_ON(!dst->display); 2261f507968SLaurent Pinchart return; 2271f507968SLaurent Pinchart } 2281f507968SLaurent Pinchart 2293be0f15bSLaurent Pinchart WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED); 2303be0f15bSLaurent Pinchart 231db0fefd1SLaurent Pinchart if (dst->ops && dst->ops->disconnect) 232511afb44SLaurent Pinchart dst->ops->disconnect(src, dst); 233511afb44SLaurent Pinchart dst->dss = NULL; 234ec727e3fSLaurent Pinchart } 235ec727e3fSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_disconnect); 236ec727e3fSLaurent Pinchart 23719b4200dSLaurent Pinchart void omapdss_device_enable(struct omap_dss_device *dssdev) 23819b4200dSLaurent Pinchart { 23919b4200dSLaurent Pinchart if (!dssdev) 24019b4200dSLaurent Pinchart return; 24119b4200dSLaurent Pinchart 242db0fefd1SLaurent Pinchart if (dssdev->ops && dssdev->ops->enable) 24319b4200dSLaurent Pinchart dssdev->ops->enable(dssdev); 24419b4200dSLaurent Pinchart 24519b4200dSLaurent Pinchart omapdss_device_enable(dssdev->next); 24619b4200dSLaurent Pinchart 24719b4200dSLaurent Pinchart dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 24819b4200dSLaurent Pinchart } 24919b4200dSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_enable); 25019b4200dSLaurent Pinchart 25119b4200dSLaurent Pinchart void omapdss_device_disable(struct omap_dss_device *dssdev) 25219b4200dSLaurent Pinchart { 25319b4200dSLaurent Pinchart if (!dssdev) 25419b4200dSLaurent Pinchart return; 25519b4200dSLaurent Pinchart 25619b4200dSLaurent Pinchart omapdss_device_disable(dssdev->next); 25719b4200dSLaurent Pinchart 258db0fefd1SLaurent Pinchart if (dssdev->ops && dssdev->ops->disable) 25919b4200dSLaurent Pinchart dssdev->ops->disable(dssdev); 26019b4200dSLaurent Pinchart } 26119b4200dSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_disable); 26219b4200dSLaurent Pinchart 2636a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 2646a7c5a22SLaurent Pinchart * Components Handling 2656a7c5a22SLaurent Pinchart */ 2666a7c5a22SLaurent Pinchart 2676a7c5a22SLaurent Pinchart static struct list_head omapdss_comp_list; 2686a7c5a22SLaurent Pinchart 2696a7c5a22SLaurent Pinchart struct omapdss_comp_node { 2706a7c5a22SLaurent Pinchart struct list_head list; 2716a7c5a22SLaurent Pinchart struct device_node *node; 2726a7c5a22SLaurent Pinchart bool dss_core_component; 2734e17763cSLaurent Pinchart const char *compat; 2746a7c5a22SLaurent Pinchart }; 2756a7c5a22SLaurent Pinchart 2761e08c822SPeter Ujfalusi static bool omapdss_list_contains(const struct device_node *node) 2771e08c822SPeter Ujfalusi { 2781e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 2791e08c822SPeter Ujfalusi 2801e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 2811e08c822SPeter Ujfalusi if (comp->node == node) 2821e08c822SPeter Ujfalusi return true; 2831e08c822SPeter Ujfalusi } 2841e08c822SPeter Ujfalusi 2851e08c822SPeter Ujfalusi return false; 2861e08c822SPeter Ujfalusi } 2871e08c822SPeter Ujfalusi 2881e08c822SPeter Ujfalusi static void omapdss_walk_device(struct device *dev, struct device_node *node, 2891e08c822SPeter Ujfalusi bool dss_core) 2901e08c822SPeter Ujfalusi { 2914e17763cSLaurent Pinchart struct omapdss_comp_node *comp; 2921e08c822SPeter Ujfalusi struct device_node *n; 2934e17763cSLaurent Pinchart const char *compat; 2944e17763cSLaurent Pinchart int ret; 2951e08c822SPeter Ujfalusi 2964e17763cSLaurent Pinchart ret = of_property_read_string(node, "compatible", &compat); 2974e17763cSLaurent Pinchart if (ret < 0) 2984e17763cSLaurent Pinchart return; 2994e17763cSLaurent Pinchart 3004e17763cSLaurent Pinchart comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); 3011e08c822SPeter Ujfalusi if (comp) { 3021e08c822SPeter Ujfalusi comp->node = node; 3031e08c822SPeter Ujfalusi comp->dss_core_component = dss_core; 3044e17763cSLaurent Pinchart comp->compat = compat; 3051e08c822SPeter Ujfalusi list_add(&comp->list, &omapdss_comp_list); 3061e08c822SPeter Ujfalusi } 3071e08c822SPeter Ujfalusi 3081e08c822SPeter Ujfalusi /* 3091e08c822SPeter Ujfalusi * of_graph_get_remote_port_parent() prints an error if there is no 3101e08c822SPeter Ujfalusi * port/ports node. To avoid that, check first that there's the node. 3111e08c822SPeter Ujfalusi */ 3121e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "ports"); 3131e08c822SPeter Ujfalusi if (!n) 3141e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "port"); 3151e08c822SPeter Ujfalusi if (!n) 3161e08c822SPeter Ujfalusi return; 3171e08c822SPeter Ujfalusi 3181e08c822SPeter Ujfalusi of_node_put(n); 3191e08c822SPeter Ujfalusi 3201e08c822SPeter Ujfalusi n = NULL; 3211e08c822SPeter Ujfalusi while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 3221e08c822SPeter Ujfalusi struct device_node *pn = of_graph_get_remote_port_parent(n); 3231e08c822SPeter Ujfalusi 3241e08c822SPeter Ujfalusi if (!pn) 3251e08c822SPeter Ujfalusi continue; 3261e08c822SPeter Ujfalusi 3271e08c822SPeter Ujfalusi if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 3281e08c822SPeter Ujfalusi of_node_put(pn); 3291e08c822SPeter Ujfalusi continue; 3301e08c822SPeter Ujfalusi } 3311e08c822SPeter Ujfalusi 3321e08c822SPeter Ujfalusi omapdss_walk_device(dev, pn, false); 3331e08c822SPeter Ujfalusi } 3341e08c822SPeter Ujfalusi } 3351e08c822SPeter Ujfalusi 3361e08c822SPeter Ujfalusi void omapdss_gather_components(struct device *dev) 3371e08c822SPeter Ujfalusi { 3381e08c822SPeter Ujfalusi struct device_node *child; 3391e08c822SPeter Ujfalusi 3401e08c822SPeter Ujfalusi INIT_LIST_HEAD(&omapdss_comp_list); 3411e08c822SPeter Ujfalusi 3421e08c822SPeter Ujfalusi omapdss_walk_device(dev, dev->of_node, true); 3431e08c822SPeter Ujfalusi 3444e17763cSLaurent Pinchart for_each_available_child_of_node(dev->of_node, child) 3451e08c822SPeter Ujfalusi omapdss_walk_device(dev, child, true); 3461e08c822SPeter Ujfalusi } 3471e08c822SPeter Ujfalusi EXPORT_SYMBOL(omapdss_gather_components); 3481e08c822SPeter Ujfalusi 3491e08c822SPeter Ujfalusi static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp) 3501e08c822SPeter Ujfalusi { 3511e08c822SPeter Ujfalusi if (comp->dss_core_component) 3521e08c822SPeter Ujfalusi return true; 3534e17763cSLaurent Pinchart if (!strstarts(comp->compat, "omapdss,")) 3544e17763cSLaurent Pinchart return true; 3559184f8d9SLaurent Pinchart if (omapdss_device_is_registered(comp->node)) 3561e08c822SPeter Ujfalusi return true; 3571e08c822SPeter Ujfalusi 3581e08c822SPeter Ujfalusi return false; 3591e08c822SPeter Ujfalusi } 3601e08c822SPeter Ujfalusi 3611e08c822SPeter Ujfalusi bool omapdss_stack_is_ready(void) 3621e08c822SPeter Ujfalusi { 3631e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 3641e08c822SPeter Ujfalusi 3651e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 3661e08c822SPeter Ujfalusi if (!omapdss_component_is_loaded(comp)) 3671e08c822SPeter Ujfalusi return false; 3681e08c822SPeter Ujfalusi } 3691e08c822SPeter Ujfalusi 3701e08c822SPeter Ujfalusi return true; 3711e08c822SPeter Ujfalusi } 3721e08c822SPeter Ujfalusi EXPORT_SYMBOL(omapdss_stack_is_ready); 3731e08c822SPeter Ujfalusi 374a99ac0d9STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 375a99ac0d9STomi Valkeinen MODULE_DESCRIPTION("OMAP Display Subsystem Base"); 376a99ac0d9STomi Valkeinen MODULE_LICENSE("GPL v2"); 377