234 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2012 Sascha Hauer, Pengutronix4 * Copyright 2019,2020,2022 NXP5 */6 7#include <linux/export.h>8#include <linux/media-bus-format.h>9#include <linux/mfd/syscon.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/regmap.h>13 14#include <drm/drm_bridge.h>15#include <drm/drm_of.h>16#include <drm/drm_print.h>17 18#include "imx-ldb-helper.h"19 20bool ldb_channel_is_single_link(struct ldb_channel *ldb_ch)21{22 return ldb_ch->link_type == LDB_CH_SINGLE_LINK;23}24EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);25 26bool ldb_channel_is_split_link(struct ldb_channel *ldb_ch)27{28 return ldb_ch->link_type == LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS ||29 ldb_ch->link_type == LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;30}31EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);32 33int ldb_bridge_atomic_check_helper(struct drm_bridge *bridge,34 struct drm_bridge_state *bridge_state,35 struct drm_crtc_state *crtc_state,36 struct drm_connector_state *conn_state)37{38 struct ldb_channel *ldb_ch = bridge->driver_private;39 40 ldb_ch->in_bus_format = bridge_state->input_bus_cfg.format;41 ldb_ch->out_bus_format = bridge_state->output_bus_cfg.format;42 43 return 0;44}45EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);46 47void ldb_bridge_mode_set_helper(struct drm_bridge *bridge,48 const struct drm_display_mode *mode,49 const struct drm_display_mode *adjusted_mode)50{51 struct ldb_channel *ldb_ch = bridge->driver_private;52 struct ldb *ldb = ldb_ch->ldb;53 bool is_split = ldb_channel_is_split_link(ldb_ch);54 55 if (is_split)56 ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN;57 58 switch (ldb_ch->out_bus_format) {59 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:60 break;61 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:62 if (ldb_ch->chno == 0 || is_split)63 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;64 if (ldb_ch->chno == 1 || is_split)65 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;66 break;67 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:68 if (ldb_ch->chno == 0 || is_split)69 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |70 LDB_BIT_MAP_CH0_JEIDA;71 if (ldb_ch->chno == 1 || is_split)72 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |73 LDB_BIT_MAP_CH1_JEIDA;74 break;75 }76}77EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);78 79void ldb_bridge_enable_helper(struct drm_bridge *bridge)80{81 struct ldb_channel *ldb_ch = bridge->driver_private;82 struct ldb *ldb = ldb_ch->ldb;83 84 /*85 * Platform specific bridge drivers should set ldb_ctrl properly86 * for the enablement, so just write the ctrl_reg here.87 */88 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);89}90EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);91 92void ldb_bridge_disable_helper(struct drm_bridge *bridge)93{94 struct ldb_channel *ldb_ch = bridge->driver_private;95 struct ldb *ldb = ldb_ch->ldb;96 bool is_split = ldb_channel_is_split_link(ldb_ch);97 98 if (ldb_ch->chno == 0 || is_split)99 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;100 if (ldb_ch->chno == 1 || is_split)101 ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;102 103 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);104}105EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);106 107int ldb_bridge_attach_helper(struct drm_bridge *bridge,108 enum drm_bridge_attach_flags flags)109{110 struct ldb_channel *ldb_ch = bridge->driver_private;111 struct ldb *ldb = ldb_ch->ldb;112 113 if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {114 DRM_DEV_ERROR(ldb->dev,115 "do not support creating a drm_connector\n");116 return -EINVAL;117 }118 119 return drm_bridge_attach(bridge->encoder,120 ldb_ch->next_bridge, bridge,121 DRM_BRIDGE_ATTACH_NO_CONNECTOR);122}123EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);124 125int ldb_init_helper(struct ldb *ldb)126{127 struct device *dev = ldb->dev;128 struct device_node *np = dev->of_node;129 struct device_node *child;130 int ret;131 u32 i;132 133 ldb->regmap = syscon_node_to_regmap(np->parent);134 if (IS_ERR(ldb->regmap)) {135 ret = PTR_ERR(ldb->regmap);136 if (ret != -EPROBE_DEFER)137 DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret);138 return ret;139 }140 141 for_each_available_child_of_node(np, child) {142 struct ldb_channel *ldb_ch;143 144 ret = of_property_read_u32(child, "reg", &i);145 if (ret || i > MAX_LDB_CHAN_NUM - 1) {146 ret = -EINVAL;147 DRM_DEV_ERROR(dev,148 "invalid channel node address: %u\n", i);149 of_node_put(child);150 return ret;151 }152 153 ldb_ch = ldb->channel[i];154 ldb_ch->ldb = ldb;155 ldb_ch->chno = i;156 ldb_ch->is_available = true;157 ldb_ch->np = child;158 159 ldb->available_ch_cnt++;160 }161 162 return 0;163}164EXPORT_SYMBOL_GPL(ldb_init_helper);165 166int ldb_find_next_bridge_helper(struct ldb *ldb)167{168 struct device *dev = ldb->dev;169 struct ldb_channel *ldb_ch;170 int ret, i;171 172 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {173 ldb_ch = ldb->channel[i];174 175 if (!ldb_ch->is_available)176 continue;177 178 ldb_ch->next_bridge = devm_drm_of_get_bridge(dev, ldb_ch->np,179 1, 0);180 if (IS_ERR(ldb_ch->next_bridge)) {181 ret = PTR_ERR(ldb_ch->next_bridge);182 if (ret != -EPROBE_DEFER)183 DRM_DEV_ERROR(dev,184 "failed to get next bridge: %d\n",185 ret);186 return ret;187 }188 }189 190 return 0;191}192EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);193 194void ldb_add_bridge_helper(struct ldb *ldb,195 const struct drm_bridge_funcs *bridge_funcs)196{197 struct ldb_channel *ldb_ch;198 int i;199 200 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {201 ldb_ch = ldb->channel[i];202 203 if (!ldb_ch->is_available)204 continue;205 206 ldb_ch->bridge.driver_private = ldb_ch;207 ldb_ch->bridge.funcs = bridge_funcs;208 ldb_ch->bridge.of_node = ldb_ch->np;209 210 drm_bridge_add(&ldb_ch->bridge);211 }212}213EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);214 215void ldb_remove_bridge_helper(struct ldb *ldb)216{217 struct ldb_channel *ldb_ch;218 int i;219 220 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {221 ldb_ch = ldb->channel[i];222 223 if (!ldb_ch->is_available)224 continue;225 226 drm_bridge_remove(&ldb_ch->bridge);227 }228}229EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);230 231MODULE_DESCRIPTION("i.MX8 LVDS Display Bridge(LDB)/Pixel Mapper bridge helper");232MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");233MODULE_LICENSE("GPL");234