1 | /* |
2 | * Copyright 2012 Red Hat Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the |
6 | * "Software"), to deal in the Software without restriction, including |
7 | * without limitation the rights to use, copy, modify, merge, publish, |
8 | * distribute, sub license, and/or sell copies of the Software, and to |
9 | * permit persons to whom the Software is furnished to do so, subject to |
10 | * the following conditions: |
11 | * |
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
13 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
14 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
15 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
16 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
17 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
18 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
19 | * |
20 | * The above copyright notice and this permission notice (including the |
21 | * next paragraph) shall be included in all copies or substantial portions |
22 | * of the Software. |
23 | * |
24 | */ |
25 | /* |
26 | * Authors: Dave Airlie <airlied@redhat.com> |
27 | */ |
28 | |
29 | #include <linux/pci.h> |
30 | |
31 | #include <drm/drm_atomic_helper.h> |
32 | #include <drm/drm_drv.h> |
33 | #include <drm/drm_gem.h> |
34 | #include <drm/drm_managed.h> |
35 | |
36 | #include "ast_drv.h" |
37 | |
38 | static void ast_detect_widescreen(struct ast_device *ast) |
39 | { |
40 | u8 jreg; |
41 | |
42 | /* Check if we support wide screen */ |
43 | switch (AST_GEN(ast)) { |
44 | case 1: |
45 | ast->support_wide_screen = false; |
46 | break; |
47 | default: |
48 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd0, preserve_mask: 0xff); |
49 | if (!(jreg & 0x80)) |
50 | ast->support_wide_screen = true; |
51 | else if (jreg & 0x01) |
52 | ast->support_wide_screen = true; |
53 | else { |
54 | ast->support_wide_screen = false; |
55 | if (ast->chip == AST1300) |
56 | ast->support_wide_screen = true; |
57 | if (ast->chip == AST1400) |
58 | ast->support_wide_screen = true; |
59 | if (ast->chip == AST2510) |
60 | ast->support_wide_screen = true; |
61 | if (IS_AST_GEN7(ast)) |
62 | ast->support_wide_screen = true; |
63 | } |
64 | break; |
65 | } |
66 | } |
67 | |
68 | static void ast_detect_tx_chip(struct ast_device *ast, bool need_post) |
69 | { |
70 | struct drm_device *dev = &ast->base; |
71 | u8 jreg; |
72 | |
73 | /* Check 3rd Tx option (digital output afaik) */ |
74 | ast->tx_chip_types |= AST_TX_NONE_BIT; |
75 | |
76 | /* |
77 | * VGACRA3 Enhanced Color Mode Register, check if DVO is already |
78 | * enabled, in that case, assume we have a SIL164 TMDS transmitter |
79 | * |
80 | * Don't make that assumption if we the chip wasn't enabled and |
81 | * is at power-on reset, otherwise we'll incorrectly "detect" a |
82 | * SIL164 when there is none. |
83 | */ |
84 | if (!need_post) { |
85 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xa3, preserve_mask: 0xff); |
86 | if (jreg & 0x80) |
87 | ast->tx_chip_types = AST_TX_SIL164_BIT; |
88 | } |
89 | |
90 | if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || IS_AST_GEN6(ast)) { |
91 | /* |
92 | * On AST GEN4+, look the configuration set by the SoC in |
93 | * the SOC scratch register #1 bits 11:8 (interestingly marked |
94 | * as "reserved" in the spec) |
95 | */ |
96 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd1, preserve_mask: 0xff); |
97 | switch (jreg) { |
98 | case 0x04: |
99 | ast->tx_chip_types = AST_TX_SIL164_BIT; |
100 | break; |
101 | case 0x08: |
102 | ast->dp501_fw_addr = drmm_kzalloc(dev, size: 32*1024, GFP_KERNEL); |
103 | if (ast->dp501_fw_addr) { |
104 | /* backup firmware */ |
105 | if (ast_backup_fw(dev, addr: ast->dp501_fw_addr, size: 32*1024)) { |
106 | drmm_kfree(dev, data: ast->dp501_fw_addr); |
107 | ast->dp501_fw_addr = NULL; |
108 | } |
109 | } |
110 | fallthrough; |
111 | case 0x0c: |
112 | ast->tx_chip_types = AST_TX_DP501_BIT; |
113 | } |
114 | } else if (IS_AST_GEN7(ast)) { |
115 | if (ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD1, TX_TYPE_MASK) == |
116 | ASTDP_DPMCU_TX) { |
117 | ast->tx_chip_types = AST_TX_ASTDP_BIT; |
118 | ast_dp_launch(dev: &ast->base); |
119 | } |
120 | } |
121 | |
122 | /* Print stuff for diagnostic purposes */ |
123 | if (ast->tx_chip_types & AST_TX_NONE_BIT) |
124 | drm_info(dev, "Using analog VGA\n" ); |
125 | if (ast->tx_chip_types & AST_TX_SIL164_BIT) |
126 | drm_info(dev, "Using Sil164 TMDS transmitter\n" ); |
127 | if (ast->tx_chip_types & AST_TX_DP501_BIT) |
128 | drm_info(dev, "Using DP501 DisplayPort transmitter\n" ); |
129 | if (ast->tx_chip_types & AST_TX_ASTDP_BIT) |
130 | drm_info(dev, "Using ASPEED DisplayPort transmitter\n" ); |
131 | } |
132 | |
133 | static int ast_get_dram_info(struct drm_device *dev) |
134 | { |
135 | struct device_node *np = dev->dev->of_node; |
136 | struct ast_device *ast = to_ast_device(dev); |
137 | uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap; |
138 | uint32_t denum, num, div, ref_pll, dsel; |
139 | |
140 | switch (ast->config_mode) { |
141 | case ast_use_dt: |
142 | /* |
143 | * If some properties are missing, use reasonable |
144 | * defaults for GEN5 |
145 | */ |
146 | if (of_property_read_u32(np, propname: "aspeed,mcr-configuration" , |
147 | out_value: &mcr_cfg)) |
148 | mcr_cfg = 0x00000577; |
149 | if (of_property_read_u32(np, propname: "aspeed,mcr-scu-mpll" , |
150 | out_value: &mcr_scu_mpll)) |
151 | mcr_scu_mpll = 0x000050C0; |
152 | if (of_property_read_u32(np, propname: "aspeed,mcr-scu-strap" , |
153 | out_value: &mcr_scu_strap)) |
154 | mcr_scu_strap = 0; |
155 | break; |
156 | case ast_use_p2a: |
157 | ast_write32(ast, reg: 0xf004, val: 0x1e6e0000); |
158 | ast_write32(ast, reg: 0xf000, val: 0x1); |
159 | mcr_cfg = ast_read32(ast, reg: 0x10004); |
160 | mcr_scu_mpll = ast_read32(ast, reg: 0x10120); |
161 | mcr_scu_strap = ast_read32(ast, reg: 0x10170); |
162 | break; |
163 | case ast_use_defaults: |
164 | default: |
165 | ast->dram_bus_width = 16; |
166 | ast->dram_type = AST_DRAM_1Gx16; |
167 | if (IS_AST_GEN6(ast)) |
168 | ast->mclk = 800; |
169 | else |
170 | ast->mclk = 396; |
171 | return 0; |
172 | } |
173 | |
174 | if (mcr_cfg & 0x40) |
175 | ast->dram_bus_width = 16; |
176 | else |
177 | ast->dram_bus_width = 32; |
178 | |
179 | if (IS_AST_GEN6(ast)) { |
180 | switch (mcr_cfg & 0x03) { |
181 | case 0: |
182 | ast->dram_type = AST_DRAM_1Gx16; |
183 | break; |
184 | default: |
185 | case 1: |
186 | ast->dram_type = AST_DRAM_2Gx16; |
187 | break; |
188 | case 2: |
189 | ast->dram_type = AST_DRAM_4Gx16; |
190 | break; |
191 | case 3: |
192 | ast->dram_type = AST_DRAM_8Gx16; |
193 | break; |
194 | } |
195 | } else if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { |
196 | switch (mcr_cfg & 0x03) { |
197 | case 0: |
198 | ast->dram_type = AST_DRAM_512Mx16; |
199 | break; |
200 | default: |
201 | case 1: |
202 | ast->dram_type = AST_DRAM_1Gx16; |
203 | break; |
204 | case 2: |
205 | ast->dram_type = AST_DRAM_2Gx16; |
206 | break; |
207 | case 3: |
208 | ast->dram_type = AST_DRAM_4Gx16; |
209 | break; |
210 | } |
211 | } else { |
212 | switch (mcr_cfg & 0x0c) { |
213 | case 0: |
214 | case 4: |
215 | ast->dram_type = AST_DRAM_512Mx16; |
216 | break; |
217 | case 8: |
218 | if (mcr_cfg & 0x40) |
219 | ast->dram_type = AST_DRAM_1Gx16; |
220 | else |
221 | ast->dram_type = AST_DRAM_512Mx32; |
222 | break; |
223 | case 0xc: |
224 | ast->dram_type = AST_DRAM_1Gx32; |
225 | break; |
226 | } |
227 | } |
228 | |
229 | if (mcr_scu_strap & 0x2000) |
230 | ref_pll = 14318; |
231 | else |
232 | ref_pll = 12000; |
233 | |
234 | denum = mcr_scu_mpll & 0x1f; |
235 | num = (mcr_scu_mpll & 0x3fe0) >> 5; |
236 | dsel = (mcr_scu_mpll & 0xc000) >> 14; |
237 | switch (dsel) { |
238 | case 3: |
239 | div = 0x4; |
240 | break; |
241 | case 2: |
242 | case 1: |
243 | div = 0x2; |
244 | break; |
245 | default: |
246 | div = 0x1; |
247 | break; |
248 | } |
249 | ast->mclk = ref_pll * (num + 2) / ((denum + 2) * (div * 1000)); |
250 | return 0; |
251 | } |
252 | |
253 | struct drm_device *ast_device_create(struct pci_dev *pdev, |
254 | const struct drm_driver *drv, |
255 | enum ast_chip chip, |
256 | enum ast_config_mode config_mode, |
257 | void __iomem *regs, |
258 | void __iomem *ioregs, |
259 | bool need_post) |
260 | { |
261 | struct drm_device *dev; |
262 | struct ast_device *ast; |
263 | int ret; |
264 | |
265 | ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); |
266 | if (IS_ERR(ptr: ast)) |
267 | return ERR_CAST(ptr: ast); |
268 | dev = &ast->base; |
269 | |
270 | ast->chip = chip; |
271 | ast->config_mode = config_mode; |
272 | ast->regs = regs; |
273 | ast->ioregs = ioregs; |
274 | |
275 | ast_detect_widescreen(ast); |
276 | ast_detect_tx_chip(ast, need_post); |
277 | |
278 | ret = ast_get_dram_info(dev); |
279 | if (ret) |
280 | return ERR_PTR(error: ret); |
281 | |
282 | drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n" , |
283 | ast->mclk, ast->dram_type, ast->dram_bus_width); |
284 | |
285 | if (need_post) |
286 | ast_post_gpu(dev); |
287 | |
288 | ret = ast_mm_init(ast); |
289 | if (ret) |
290 | return ERR_PTR(error: ret); |
291 | |
292 | /* map reserved buffer */ |
293 | ast->dp501_fw_buf = NULL; |
294 | if (ast->vram_size < pci_resource_len(pdev, 0)) { |
295 | ast->dp501_fw_buf = pci_iomap_range(dev: pdev, bar: 0, offset: ast->vram_size, maxlen: 0); |
296 | if (!ast->dp501_fw_buf) |
297 | drm_info(dev, "failed to map reserved buffer!\n" ); |
298 | } |
299 | |
300 | ret = ast_mode_config_init(ast); |
301 | if (ret) |
302 | return ERR_PTR(error: ret); |
303 | |
304 | return dev; |
305 | } |
306 | |