Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2017 Free Electrons
  4 * Maxime Ripard <maxime.ripard@free-electrons.com>
  5 */
  6
  7#include <linux/clk.h>
  8
 
  9#include <drm/drm_atomic_helper.h>
 10#include <drm/drm_bridge.h>
 11#include <drm/drm_of.h>
 12#include <drm/drm_panel.h>
 13#include <drm/drm_print.h>
 14#include <drm/drm_probe_helper.h>
 15#include <drm/drm_simple_kms_helper.h>
 16
 17#include "sun4i_crtc.h"
 18#include "sun4i_tcon.h"
 19#include "sun4i_lvds.h"
 20
 21struct sun4i_lvds {
 22	struct drm_connector	connector;
 23	struct drm_encoder	encoder;
 24
 25	struct drm_panel	*panel;
 26};
 27
 28static inline struct sun4i_lvds *
 29drm_connector_to_sun4i_lvds(struct drm_connector *connector)
 30{
 31	return container_of(connector, struct sun4i_lvds,
 32			    connector);
 33}
 34
 35static inline struct sun4i_lvds *
 36drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
 37{
 38	return container_of(encoder, struct sun4i_lvds,
 39			    encoder);
 40}
 41
 42static int sun4i_lvds_get_modes(struct drm_connector *connector)
 43{
 44	struct sun4i_lvds *lvds =
 45		drm_connector_to_sun4i_lvds(connector);
 
 46
 47	return drm_panel_get_modes(lvds->panel, connector);
 48}
 49
 50static const struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
 51	.get_modes	= sun4i_lvds_get_modes,
 52};
 53
 54static void
 55sun4i_lvds_connector_destroy(struct drm_connector *connector)
 56{
 
 
 
 
 57	drm_connector_cleanup(connector);
 58}
 59
 60static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
 61	.fill_modes		= drm_helper_probe_single_connector_modes,
 62	.destroy		= sun4i_lvds_connector_destroy,
 63	.reset			= drm_atomic_helper_connector_reset,
 64	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
 65	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
 66};
 67
 68static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
 69{
 70	struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
 
 71
 72	DRM_DEBUG_DRIVER("Enabling LVDS output\n");
 73
 74	if (lvds->panel) {
 75		drm_panel_prepare(lvds->panel);
 76		drm_panel_enable(lvds->panel);
 77	}
 78}
 79
 80static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
 81{
 82	struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
 
 83
 84	DRM_DEBUG_DRIVER("Disabling LVDS output\n");
 85
 86	if (lvds->panel) {
 87		drm_panel_disable(lvds->panel);
 88		drm_panel_unprepare(lvds->panel);
 89	}
 90}
 91
 92static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
 93	.disable	= sun4i_lvds_encoder_disable,
 94	.enable		= sun4i_lvds_encoder_enable,
 95};
 96
 
 
 
 
 97int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
 98{
 99	struct drm_encoder *encoder;
100	struct drm_bridge *bridge;
101	struct sun4i_lvds *lvds;
102	int ret;
103
104	lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
105	if (!lvds)
106		return -ENOMEM;
 
107	encoder = &lvds->encoder;
108
109	ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
110					  &lvds->panel, &bridge);
111	if (ret) {
112		dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
113		return 0;
114	}
115
116	drm_encoder_helper_add(&lvds->encoder,
117			       &sun4i_lvds_enc_helper_funcs);
118	ret = drm_simple_encoder_init(drm, &lvds->encoder,
119				      DRM_MODE_ENCODER_LVDS);
 
 
 
120	if (ret) {
121		dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
122		goto err_out;
123	}
124
125	/* The LVDS encoder can only work with the TCON channel 0 */
126	lvds->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
127
128	if (lvds->panel) {
129		drm_connector_helper_add(&lvds->connector,
130					 &sun4i_lvds_con_helper_funcs);
131		ret = drm_connector_init(drm, &lvds->connector,
132					 &sun4i_lvds_con_funcs,
133					 DRM_MODE_CONNECTOR_LVDS);
134		if (ret) {
135			dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
136			goto err_cleanup_connector;
137		}
138
139		drm_connector_attach_encoder(&lvds->connector,
140						  &lvds->encoder);
 
 
 
 
 
 
141	}
142
143	if (bridge) {
144		ret = drm_bridge_attach(encoder, bridge, NULL, 0);
145		if (ret)
 
146			goto err_cleanup_connector;
 
147	}
148
149	return 0;
150
151err_cleanup_connector:
152	drm_encoder_cleanup(&lvds->encoder);
153err_out:
154	return ret;
155}
156EXPORT_SYMBOL(sun4i_lvds_init);
v4.17
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2017 Free Electrons
  4 * Maxime Ripard <maxime.ripard@free-electrons.com>
  5 */
  6
  7#include <linux/clk.h>
  8
  9#include <drm/drmP.h>
 10#include <drm/drm_atomic_helper.h>
 11#include <drm/drm_crtc_helper.h>
 12#include <drm/drm_of.h>
 13#include <drm/drm_panel.h>
 
 
 
 14
 15#include "sun4i_crtc.h"
 16#include "sun4i_tcon.h"
 17#include "sun4i_lvds.h"
 18
 19struct sun4i_lvds {
 20	struct drm_connector	connector;
 21	struct drm_encoder	encoder;
 22
 23	struct sun4i_tcon	*tcon;
 24};
 25
 26static inline struct sun4i_lvds *
 27drm_connector_to_sun4i_lvds(struct drm_connector *connector)
 28{
 29	return container_of(connector, struct sun4i_lvds,
 30			    connector);
 31}
 32
 33static inline struct sun4i_lvds *
 34drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
 35{
 36	return container_of(encoder, struct sun4i_lvds,
 37			    encoder);
 38}
 39
 40static int sun4i_lvds_get_modes(struct drm_connector *connector)
 41{
 42	struct sun4i_lvds *lvds =
 43		drm_connector_to_sun4i_lvds(connector);
 44	struct sun4i_tcon *tcon = lvds->tcon;
 45
 46	return drm_panel_get_modes(tcon->panel);
 47}
 48
 49static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
 50	.get_modes	= sun4i_lvds_get_modes,
 51};
 52
 53static void
 54sun4i_lvds_connector_destroy(struct drm_connector *connector)
 55{
 56	struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
 57	struct sun4i_tcon *tcon = lvds->tcon;
 58
 59	drm_panel_detach(tcon->panel);
 60	drm_connector_cleanup(connector);
 61}
 62
 63static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
 64	.fill_modes		= drm_helper_probe_single_connector_modes,
 65	.destroy		= sun4i_lvds_connector_destroy,
 66	.reset			= drm_atomic_helper_connector_reset,
 67	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
 68	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
 69};
 70
 71static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
 72{
 73	struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
 74	struct sun4i_tcon *tcon = lvds->tcon;
 75
 76	DRM_DEBUG_DRIVER("Enabling LVDS output\n");
 77
 78	if (!IS_ERR(tcon->panel)) {
 79		drm_panel_prepare(tcon->panel);
 80		drm_panel_enable(tcon->panel);
 81	}
 82}
 83
 84static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
 85{
 86	struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
 87	struct sun4i_tcon *tcon = lvds->tcon;
 88
 89	DRM_DEBUG_DRIVER("Disabling LVDS output\n");
 90
 91	if (!IS_ERR(tcon->panel)) {
 92		drm_panel_disable(tcon->panel);
 93		drm_panel_unprepare(tcon->panel);
 94	}
 95}
 96
 97static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
 98	.disable	= sun4i_lvds_encoder_disable,
 99	.enable		= sun4i_lvds_encoder_enable,
100};
101
102static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
103	.destroy	= drm_encoder_cleanup,
104};
105
106int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
107{
108	struct drm_encoder *encoder;
109	struct drm_bridge *bridge;
110	struct sun4i_lvds *lvds;
111	int ret;
112
113	lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
114	if (!lvds)
115		return -ENOMEM;
116	lvds->tcon = tcon;
117	encoder = &lvds->encoder;
118
119	ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
120					  &tcon->panel, &bridge);
121	if (ret) {
122		dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
123		return 0;
124	}
125
126	drm_encoder_helper_add(&lvds->encoder,
127			       &sun4i_lvds_enc_helper_funcs);
128	ret = drm_encoder_init(drm,
129			       &lvds->encoder,
130			       &sun4i_lvds_enc_funcs,
131			       DRM_MODE_ENCODER_LVDS,
132			       NULL);
133	if (ret) {
134		dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
135		goto err_out;
136	}
137
138	/* The LVDS encoder can only work with the TCON channel 0 */
139	lvds->encoder.possible_crtcs = BIT(drm_crtc_index(&tcon->crtc->crtc));
140
141	if (tcon->panel) {
142		drm_connector_helper_add(&lvds->connector,
143					 &sun4i_lvds_con_helper_funcs);
144		ret = drm_connector_init(drm, &lvds->connector,
145					 &sun4i_lvds_con_funcs,
146					 DRM_MODE_CONNECTOR_LVDS);
147		if (ret) {
148			dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
149			goto err_cleanup_connector;
150		}
151
152		drm_mode_connector_attach_encoder(&lvds->connector,
153						  &lvds->encoder);
154
155		ret = drm_panel_attach(tcon->panel, &lvds->connector);
156		if (ret) {
157			dev_err(drm->dev, "Couldn't attach our panel\n");
158			goto err_cleanup_connector;
159		}
160	}
161
162	if (bridge) {
163		ret = drm_bridge_attach(encoder, bridge, NULL);
164		if (ret) {
165			dev_err(drm->dev, "Couldn't attach our bridge\n");
166			goto err_cleanup_connector;
167		}
168	}
169
170	return 0;
171
172err_cleanup_connector:
173	drm_encoder_cleanup(&lvds->encoder);
174err_out:
175	return ret;
176}
177EXPORT_SYMBOL(sun4i_lvds_init);