forked from analogdevicesinc/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaxi_hdmi_crtc.c
More file actions
211 lines (171 loc) · 5.68 KB
/
Copy pathaxi_hdmi_crtc.c
File metadata and controls
211 lines (171 loc) · 5.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
* Analog Devices AXI HDMI DRM driver.
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/amba/xilinx_dma.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
#include "axi_hdmi_crtc.h"
#include "axi_hdmi_drv.h"
#include "axi_hdmi_encoder.h"
struct axi_hdmi_crtc {
struct drm_crtc drm_crtc;
struct dma_chan *dma;
struct dma_interleaved_template *dma_template;
int mode;
};
static inline struct axi_hdmi_crtc *to_axi_hdmi_crtc(struct drm_crtc *crtc)
{
return container_of(crtc, struct axi_hdmi_crtc, drm_crtc);
}
static int axi_hdmi_crtc_update(struct drm_crtc *crtc)
{
struct axi_hdmi_crtc *axi_hdmi_crtc = to_axi_hdmi_crtc(crtc);
struct drm_display_mode *mode = &crtc->mode;
struct drm_framebuffer *fb = crtc->primary->fb;
struct dma_async_tx_descriptor *desc;
struct drm_gem_cma_object *obj;
struct xilinx_vdma_config vdma_config;
size_t offset, hw_row_size;
if (!mode || !fb)
return -EINVAL;
dmaengine_terminate_all(axi_hdmi_crtc->dma);
if (axi_hdmi_crtc->mode == DRM_MODE_DPMS_ON) {
obj = drm_fb_cma_get_gem_obj(fb, 0);
if (!obj)
return -EINVAL;
memset(&vdma_config, 0, sizeof(vdma_config));
vdma_config.park = 1;
xilinx_vdma_channel_set_config(axi_hdmi_crtc->dma, &vdma_config);
offset = crtc->x * fb->bits_per_pixel / 8 +
crtc->y * fb->pitches[0];
/* Interleaved DMA is used that way:
* Each interleaved frame is a row (hsize) implemented in ONE
* chunk (sgl has len 1).
* The number of interleaved frames is the number of rows (vsize).
* The icg in used to pack data to the HW, so that the buffer len
* is fb->piches[0], but the actual size for the hw is somewhat less
*/
axi_hdmi_crtc->dma_template->dir = DMA_MEM_TO_DEV;
axi_hdmi_crtc->dma_template->src_start = obj->paddr + offset;
/* sgl list have just one entry (each interleaved frame have 1 chunk) */
axi_hdmi_crtc->dma_template->frame_size = 1;
/* the number of interleaved frame, each has the size specified in sgl */
axi_hdmi_crtc->dma_template->numf = mode->vdisplay;
axi_hdmi_crtc->dma_template->src_sgl = 1;
axi_hdmi_crtc->dma_template->src_inc = 1;
/* vdma IP does not provide any addr to the hdmi IP, so dst_inc
* and dst_sgl should make no any difference.
*/
axi_hdmi_crtc->dma_template->dst_inc = 0;
axi_hdmi_crtc->dma_template->dst_sgl = 0;
hw_row_size = mode->hdisplay * fb->bits_per_pixel / 8;
axi_hdmi_crtc->dma_template->sgl[0].size = hw_row_size;
/* the vdma driver seems to look at icg, and not src_icg */
axi_hdmi_crtc->dma_template->sgl[0].icg =
fb->pitches[0] - hw_row_size;
desc = dmaengine_prep_interleaved_dma(axi_hdmi_crtc->dma,
axi_hdmi_crtc->dma_template, 0);
if (!desc) {
pr_err("Failed to prepare DMA descriptor\n");
return -ENOMEM;
} else {
dmaengine_submit(desc);
dma_async_issue_pending(axi_hdmi_crtc->dma);
}
}
return 0;
}
static void axi_hdmi_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct axi_hdmi_crtc *axi_hdmi_crtc = to_axi_hdmi_crtc(crtc);
if (axi_hdmi_crtc->mode != mode) {
axi_hdmi_crtc->mode = mode;
axi_hdmi_crtc_update(crtc);
}
}
static void axi_hdmi_crtc_prepare(struct drm_crtc *crtc)
{
struct axi_hdmi_crtc *axi_hdmi_crtc = to_axi_hdmi_crtc(crtc);
dmaengine_terminate_all(axi_hdmi_crtc->dma);
}
static void axi_hdmi_crtc_commit(struct drm_crtc *crtc)
{
struct axi_hdmi_crtc *axi_hdmi_crtc = to_axi_hdmi_crtc(crtc);
axi_hdmi_crtc->mode = DRM_MODE_DPMS_ON;
axi_hdmi_crtc_update(crtc);
}
static bool axi_hdmi_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static int axi_hdmi_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode,
int x, int y, struct drm_framebuffer *old_fb)
{
/* We do everything in commit() */
return 0;
}
static int axi_hdmi_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
return axi_hdmi_crtc_update(crtc);
}
static void axi_hdmi_crtc_load_lut(struct drm_crtc *crtc)
{
}
static struct drm_crtc_helper_funcs axi_hdmi_crtc_helper_funcs = {
.dpms = axi_hdmi_crtc_dpms,
.prepare = axi_hdmi_crtc_prepare,
.commit = axi_hdmi_crtc_commit,
.mode_fixup = axi_hdmi_crtc_mode_fixup,
.mode_set = axi_hdmi_crtc_mode_set,
.mode_set_base = axi_hdmi_crtc_mode_set_base,
.load_lut = axi_hdmi_crtc_load_lut,
};
static void axi_hdmi_crtc_destroy(struct drm_crtc *crtc)
{
struct axi_hdmi_crtc *axi_hdmi_crtc = to_axi_hdmi_crtc(crtc);
drm_crtc_cleanup(crtc);
kfree(axi_hdmi_crtc->dma_template);
kfree(axi_hdmi_crtc);
}
static struct drm_crtc_funcs axi_hdmi_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.destroy = axi_hdmi_crtc_destroy,
};
struct drm_crtc *axi_hdmi_crtc_create(struct drm_device *dev)
{
struct axi_hdmi_private *p = dev->dev_private;
struct axi_hdmi_crtc *axi_hdmi_crtc;
struct drm_crtc *crtc;
axi_hdmi_crtc = kzalloc(sizeof(*axi_hdmi_crtc), GFP_KERNEL);
if (!axi_hdmi_crtc) {
DRM_ERROR("failed to allocate axi_hdmi crtc\n");
return ERR_PTR(-ENOMEM);
}
/* we know we'll always use only one data chunk */
axi_hdmi_crtc->dma_template = kzalloc(
sizeof(struct dma_interleaved_template) +
sizeof(struct data_chunk), GFP_KERNEL);
if (!axi_hdmi_crtc->dma_template) {
kfree(axi_hdmi_crtc);
DRM_ERROR("failed to allocate dma_template crtc\n");
return ERR_PTR(-ENOMEM);
}
crtc = &axi_hdmi_crtc->drm_crtc;
axi_hdmi_crtc->dma = p->dma;
drm_crtc_init(dev, crtc, &axi_hdmi_crtc_funcs);
drm_crtc_helper_add(crtc, &axi_hdmi_crtc_helper_funcs);
return crtc;
}