blob: 2aac28d21f879a6108466676aa70a794090d3657 [file] [log] [blame]
Laurent Pinchart6978f122013-06-15 15:02:12 +02001/*
2 * rcar_du_encoder.c -- R-Car Display Unit Encoder
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_crtc.h>
16#include <drm/drm_crtc_helper.h>
17
18#include "rcar_du_drv.h"
19#include "rcar_du_encoder.h"
20#include "rcar_du_kms.h"
21#include "rcar_du_lvdscon.h"
22#include "rcar_du_vgacon.h"
23
24/* -----------------------------------------------------------------------------
25 * Common connector functions
26 */
27
28struct drm_encoder *
29rcar_du_connector_best_encoder(struct drm_connector *connector)
30{
31 struct rcar_du_connector *rcon = to_rcar_connector(connector);
32
33 return &rcon->encoder->encoder;
34}
35
36/* -----------------------------------------------------------------------------
37 * Encoder
38 */
39
40static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode)
41{
42}
43
44static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
45 const struct drm_display_mode *mode,
46 struct drm_display_mode *adjusted_mode)
47{
48 const struct drm_display_mode *panel_mode;
49 struct drm_device *dev = encoder->dev;
50 struct drm_connector *connector;
51 bool found = false;
52
53 /* DAC encoders have currently no restriction on the mode. */
54 if (encoder->encoder_type == DRM_MODE_ENCODER_DAC)
55 return true;
56
57 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
58 if (connector->encoder == encoder) {
59 found = true;
60 break;
61 }
62 }
63
64 if (!found) {
65 dev_dbg(dev->dev, "mode_fixup: no connector found\n");
66 return false;
67 }
68
69 if (list_empty(&connector->modes)) {
70 dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
71 return false;
72 }
73
74 panel_mode = list_first_entry(&connector->modes,
75 struct drm_display_mode, head);
76
77 /* We're not allowed to modify the resolution. */
78 if (mode->hdisplay != panel_mode->hdisplay ||
79 mode->vdisplay != panel_mode->vdisplay)
80 return false;
81
82 /* The flat panel mode is fixed, just copy it to the adjusted mode. */
83 drm_mode_copy(adjusted_mode, panel_mode);
84
85 return true;
86}
87
88static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
89{
90}
91
92static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
93{
94}
95
96static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
97 struct drm_display_mode *mode,
98 struct drm_display_mode *adjusted_mode)
99{
100 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
101
102 rcar_du_crtc_route_output(encoder->crtc, renc->output);
103}
104
105static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
106 .dpms = rcar_du_encoder_dpms,
107 .mode_fixup = rcar_du_encoder_mode_fixup,
108 .prepare = rcar_du_encoder_mode_prepare,
109 .commit = rcar_du_encoder_mode_commit,
110 .mode_set = rcar_du_encoder_mode_set,
111};
112
113static const struct drm_encoder_funcs encoder_funcs = {
114 .destroy = drm_encoder_cleanup,
115};
116
117int rcar_du_encoder_init(struct rcar_du_device *rcdu,
Laurent Pinchartef67a902013-06-17 03:13:11 +0200118 enum rcar_du_encoder_type type,
119 enum rcar_du_output output,
Laurent Pinchart6978f122013-06-15 15:02:12 +0200120 const struct rcar_du_encoder_data *data)
121{
122 struct rcar_du_encoder *renc;
Laurent Pinchartef67a902013-06-17 03:13:11 +0200123 unsigned int encoder_type;
Laurent Pinchart6978f122013-06-15 15:02:12 +0200124 int ret;
125
126 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
127 if (renc == NULL)
128 return -ENOMEM;
129
130 renc->output = output;
131
Laurent Pinchartef67a902013-06-17 03:13:11 +0200132 switch (type) {
133 case RCAR_DU_ENCODER_VGA:
134 encoder_type = DRM_MODE_ENCODER_DAC;
135 break;
136 case RCAR_DU_ENCODER_LVDS:
137 encoder_type = DRM_MODE_ENCODER_LVDS;
138 break;
139 case RCAR_DU_ENCODER_NONE:
140 default:
141 /* No external encoder, use the internal encoder type. */
142 encoder_type = rcdu->info->routes[output].encoder_type;
143 break;
144 }
145
Laurent Pinchart6978f122013-06-15 15:02:12 +0200146 ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
Laurent Pinchartef67a902013-06-17 03:13:11 +0200147 encoder_type);
Laurent Pinchart6978f122013-06-15 15:02:12 +0200148 if (ret < 0)
149 return ret;
150
151 drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
152
Laurent Pinchartef67a902013-06-17 03:13:11 +0200153 switch (encoder_type) {
154 case DRM_MODE_ENCODER_LVDS:
Laurent Pinchart6978f122013-06-15 15:02:12 +0200155 return rcar_du_lvds_connector_init(rcdu, renc,
Laurent Pinchart91947312013-06-16 16:25:35 +0200156 &data->connector.lvds.panel);
Laurent Pinchart6978f122013-06-15 15:02:12 +0200157
Laurent Pinchartef67a902013-06-17 03:13:11 +0200158 case DRM_MODE_ENCODER_DAC:
Laurent Pinchart6978f122013-06-15 15:02:12 +0200159 return rcar_du_vga_connector_init(rcdu, renc);
160
161 default:
162 return -EINVAL;
163 }
164}