Path: blob/master/drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
26517 views
// SPDX-License-Identifier: GPL-2.0+1/*2* Copyright (C) 2012 Sascha Hauer, Pengutronix3* Copyright 2019,2020,2022 NXP4*/56#include <linux/export.h>7#include <linux/media-bus-format.h>8#include <linux/mfd/syscon.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/regmap.h>1213#include <drm/drm_bridge.h>14#include <drm/drm_of.h>15#include <drm/drm_print.h>1617#include "imx-ldb-helper.h"1819bool ldb_channel_is_single_link(struct ldb_channel *ldb_ch)20{21return ldb_ch->link_type == LDB_CH_SINGLE_LINK;22}23EXPORT_SYMBOL_GPL(ldb_channel_is_single_link);2425bool ldb_channel_is_split_link(struct ldb_channel *ldb_ch)26{27return ldb_ch->link_type == LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS ||28ldb_ch->link_type == LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;29}30EXPORT_SYMBOL_GPL(ldb_channel_is_split_link);3132int ldb_bridge_atomic_check_helper(struct drm_bridge *bridge,33struct drm_bridge_state *bridge_state,34struct drm_crtc_state *crtc_state,35struct drm_connector_state *conn_state)36{37struct ldb_channel *ldb_ch = bridge->driver_private;3839ldb_ch->in_bus_format = bridge_state->input_bus_cfg.format;40ldb_ch->out_bus_format = bridge_state->output_bus_cfg.format;4142return 0;43}44EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper);4546void ldb_bridge_mode_set_helper(struct drm_bridge *bridge,47const struct drm_display_mode *mode,48const struct drm_display_mode *adjusted_mode)49{50struct ldb_channel *ldb_ch = bridge->driver_private;51struct ldb *ldb = ldb_ch->ldb;52bool is_split = ldb_channel_is_split_link(ldb_ch);5354if (is_split)55ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN;5657switch (ldb_ch->out_bus_format) {58case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:59break;60case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:61if (ldb_ch->chno == 0 || is_split)62ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;63if (ldb_ch->chno == 1 || is_split)64ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;65break;66case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:67if (ldb_ch->chno == 0 || is_split)68ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |69LDB_BIT_MAP_CH0_JEIDA;70if (ldb_ch->chno == 1 || is_split)71ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |72LDB_BIT_MAP_CH1_JEIDA;73break;74}75}76EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper);7778void ldb_bridge_enable_helper(struct drm_bridge *bridge)79{80struct ldb_channel *ldb_ch = bridge->driver_private;81struct ldb *ldb = ldb_ch->ldb;8283/*84* Platform specific bridge drivers should set ldb_ctrl properly85* for the enablement, so just write the ctrl_reg here.86*/87regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);88}89EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper);9091void ldb_bridge_disable_helper(struct drm_bridge *bridge)92{93struct ldb_channel *ldb_ch = bridge->driver_private;94struct ldb *ldb = ldb_ch->ldb;95bool is_split = ldb_channel_is_split_link(ldb_ch);9697if (ldb_ch->chno == 0 || is_split)98ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;99if (ldb_ch->chno == 1 || is_split)100ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;101102regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);103}104EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper);105106int ldb_bridge_attach_helper(struct drm_bridge *bridge, struct drm_encoder *encoder,107enum drm_bridge_attach_flags flags)108{109struct ldb_channel *ldb_ch = bridge->driver_private;110struct ldb *ldb = ldb_ch->ldb;111112if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {113DRM_DEV_ERROR(ldb->dev,114"do not support creating a drm_connector\n");115return -EINVAL;116}117118return drm_bridge_attach(encoder, ldb_ch->next_bridge, bridge,119DRM_BRIDGE_ATTACH_NO_CONNECTOR);120}121EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper);122123int ldb_init_helper(struct ldb *ldb)124{125struct device *dev = ldb->dev;126struct device_node *np = dev->of_node;127struct device_node *child;128int ret;129u32 i;130131ldb->regmap = syscon_node_to_regmap(np->parent);132if (IS_ERR(ldb->regmap)) {133ret = PTR_ERR(ldb->regmap);134if (ret != -EPROBE_DEFER)135DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret);136return ret;137}138139for_each_available_child_of_node(np, child) {140struct ldb_channel *ldb_ch;141142ret = of_property_read_u32(child, "reg", &i);143if (ret || i > MAX_LDB_CHAN_NUM - 1) {144ret = -EINVAL;145DRM_DEV_ERROR(dev,146"invalid channel node address: %u\n", i);147of_node_put(child);148return ret;149}150151ldb_ch = ldb->channel[i];152ldb_ch->ldb = ldb;153ldb_ch->chno = i;154ldb_ch->is_available = true;155ldb_ch->np = child;156157ldb->available_ch_cnt++;158}159160return 0;161}162EXPORT_SYMBOL_GPL(ldb_init_helper);163164int ldb_find_next_bridge_helper(struct ldb *ldb)165{166struct device *dev = ldb->dev;167struct ldb_channel *ldb_ch;168int ret, i;169170for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {171ldb_ch = ldb->channel[i];172173if (!ldb_ch->is_available)174continue;175176ldb_ch->next_bridge = devm_drm_of_get_bridge(dev, ldb_ch->np,1771, 0);178if (IS_ERR(ldb_ch->next_bridge)) {179ret = PTR_ERR(ldb_ch->next_bridge);180if (ret != -EPROBE_DEFER)181DRM_DEV_ERROR(dev,182"failed to get next bridge: %d\n",183ret);184return ret;185}186}187188return 0;189}190EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper);191192void ldb_add_bridge_helper(struct ldb *ldb)193{194struct ldb_channel *ldb_ch;195int i;196197for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {198ldb_ch = ldb->channel[i];199200if (!ldb_ch->is_available)201continue;202203ldb_ch->bridge.driver_private = ldb_ch;204ldb_ch->bridge.of_node = ldb_ch->np;205206drm_bridge_add(&ldb_ch->bridge);207}208}209EXPORT_SYMBOL_GPL(ldb_add_bridge_helper);210211void ldb_remove_bridge_helper(struct ldb *ldb)212{213struct ldb_channel *ldb_ch;214int i;215216for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {217ldb_ch = ldb->channel[i];218219if (!ldb_ch->is_available)220continue;221222drm_bridge_remove(&ldb_ch->bridge);223}224}225EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper);226227MODULE_DESCRIPTION("i.MX8 LVDS Display Bridge(LDB)/Pixel Mapper bridge helper");228MODULE_AUTHOR("Liu Ying <[email protected]>");229MODULE_LICENSE("GPL");230231232