170 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2016 BayLibre, SAS4 * Author: Neil Armstrong <narmstrong@baylibre.com>5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.6 */7 8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/of_graph.h>11 12#include <drm/drm_atomic_helper.h>13#include <drm/drm_simple_kms_helper.h>14#include <drm/drm_bridge.h>15#include <drm/drm_bridge_connector.h>16#include <drm/drm_device.h>17#include <drm/drm_probe_helper.h>18 19#include "meson_drv.h"20#include "meson_encoder_dsi.h"21#include "meson_registers.h"22#include "meson_venc.h"23#include "meson_vclk.h"24 25struct meson_encoder_dsi {26 struct drm_encoder encoder;27 struct drm_bridge bridge;28 struct drm_bridge *next_bridge;29 struct meson_drm *priv;30};31 32#define bridge_to_meson_encoder_dsi(x) \33 container_of(x, struct meson_encoder_dsi, bridge)34 35static int meson_encoder_dsi_attach(struct drm_bridge *bridge,36 enum drm_bridge_attach_flags flags)37{38 struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);39 40 return drm_bridge_attach(bridge->encoder, encoder_dsi->next_bridge,41 &encoder_dsi->bridge, flags);42}43 44static void meson_encoder_dsi_atomic_enable(struct drm_bridge *bridge,45 struct drm_bridge_state *bridge_state)46{47 struct meson_encoder_dsi *encoder_dsi = bridge_to_meson_encoder_dsi(bridge);48 struct drm_atomic_state *state = bridge_state->base.state;49 struct meson_drm *priv = encoder_dsi->priv;50 struct drm_connector_state *conn_state;51 struct drm_crtc_state *crtc_state;52 struct drm_connector *connector;53 54 connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);55 if (WARN_ON(!connector))56 return;57 58 conn_state = drm_atomic_get_new_connector_state(state, connector);59 if (WARN_ON(!conn_state))60 return;61 62 crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);63 if (WARN_ON(!crtc_state))64 return;65 66 /* ENCL clock setup is handled by CCF */67 68 meson_venc_mipi_dsi_mode_set(priv, &crtc_state->adjusted_mode);69 meson_encl_load_gamma(priv);70 71 writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));72 73 writel_bits_relaxed(ENCL_VIDEO_MODE_ADV_VFIFO_EN, ENCL_VIDEO_MODE_ADV_VFIFO_EN,74 priv->io_base + _REG(ENCL_VIDEO_MODE_ADV));75 writel_relaxed(0, priv->io_base + _REG(ENCL_TST_EN));76 77 writel_bits_relaxed(BIT(0), 0, priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));78 79 writel_relaxed(1, priv->io_base + _REG(ENCL_VIDEO_EN));80}81 82static void meson_encoder_dsi_atomic_disable(struct drm_bridge *bridge,83 struct drm_bridge_state *bridge_state)84{85 struct meson_encoder_dsi *meson_encoder_dsi =86 bridge_to_meson_encoder_dsi(bridge);87 struct meson_drm *priv = meson_encoder_dsi->priv;88 89 writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));90 91 writel_bits_relaxed(BIT(0), BIT(0), priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));92}93 94static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = {95 .attach = meson_encoder_dsi_attach,96 .atomic_enable = meson_encoder_dsi_atomic_enable,97 .atomic_disable = meson_encoder_dsi_atomic_disable,98 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,99 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,100 .atomic_reset = drm_atomic_helper_bridge_reset,101};102 103int meson_encoder_dsi_probe(struct meson_drm *priv)104{105 struct meson_encoder_dsi *meson_encoder_dsi;106 struct device_node *remote;107 int ret;108 109 meson_encoder_dsi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_dsi), GFP_KERNEL);110 if (!meson_encoder_dsi)111 return -ENOMEM;112 113 /* DSI Transceiver Bridge */114 remote = of_graph_get_remote_node(priv->dev->of_node, 2, 0);115 if (!remote) {116 dev_err(priv->dev, "DSI transceiver device is disabled");117 return 0;118 }119 120 meson_encoder_dsi->next_bridge = of_drm_find_bridge(remote);121 if (!meson_encoder_dsi->next_bridge)122 return dev_err_probe(priv->dev, -EPROBE_DEFER,123 "Failed to find DSI transceiver bridge\n");124 125 /* DSI Encoder Bridge */126 meson_encoder_dsi->bridge.funcs = &meson_encoder_dsi_bridge_funcs;127 meson_encoder_dsi->bridge.of_node = priv->dev->of_node;128 meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;129 130 drm_bridge_add(&meson_encoder_dsi->bridge);131 132 meson_encoder_dsi->priv = priv;133 134 /* Encoder */135 ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder,136 DRM_MODE_ENCODER_DSI);137 if (ret)138 return dev_err_probe(priv->dev, ret,139 "Failed to init DSI encoder\n");140 141 meson_encoder_dsi->encoder.possible_crtcs = BIT(0);142 143 /* Attach DSI Encoder Bridge to Encoder */144 ret = drm_bridge_attach(&meson_encoder_dsi->encoder, &meson_encoder_dsi->bridge, NULL, 0);145 if (ret)146 return dev_err_probe(priv->dev, ret,147 "Failed to attach bridge\n");148 149 /*150 * We should have now in place:151 * encoder->[dsi encoder bridge]->[dw-mipi-dsi bridge]->[panel bridge]->[panel]152 */153 154 priv->encoders[MESON_ENC_DSI] = meson_encoder_dsi;155 156 dev_dbg(priv->dev, "DSI encoder initialized\n");157 158 return 0;159}160 161void meson_encoder_dsi_remove(struct meson_drm *priv)162{163 struct meson_encoder_dsi *meson_encoder_dsi;164 165 if (priv->encoders[MESON_ENC_DSI]) {166 meson_encoder_dsi = priv->encoders[MESON_ENC_DSI];167 drm_bridge_remove(&meson_encoder_dsi->bridge);168 }169}170