/ src / drivers / aspeed / common / ast_mode_corebootfb.c
ast_mode_corebootfb.c
  1  /* SPDX-License-Identifier: MIT */
  2  /*
  3   * Copied from Linux drivers/gpu/drm/ast/ast_mode.c
  4   */
  5  
  6  #include <console/console.h>
  7  #include <edid.h>
  8  #include <device/pci_def.h>
  9  #include <framebuffer_info.h>
 10  
 11  #include "ast_drv.h"
 12  
 13  /*
 14   * Set framebuffer MMIO address, which must fall into BAR0 MMIO window.
 15   *
 16   * Complete reimplementation as the original expects multiple kernel internal
 17   * subsystems to be present.
 18   */
 19  int ast_crtc_do_set_base(struct drm_crtc *crtc)
 20  {
 21  	struct ast_private *ast = crtc->dev->dev_private;
 22  	struct drm_framebuffer *fb = crtc->primary->fb;
 23  
 24  	/* PCI BAR 0 */
 25  	struct resource *res = probe_resource(crtc->dev->pdev, PCI_BASE_ADDRESS_0);
 26  	if (!res) {
 27  		printk(BIOS_ERR, "BAR0 resource not found.\n");
 28  		return -EIO;
 29  	}
 30  
 31  	if (res->size < fb->pitches[0] * crtc->mode.vdisplay) {
 32  		dev_err(dev->pdev, "Framebuffer doesn't fit into BAR0 MMIO window\n");
 33  		return -ENOMEM;
 34  	}
 35  
 36  	fb->mmio_addr = (uintptr_t)res2mmio(res, 4095, 4095);
 37  
 38  	ast_set_offset_reg(crtc);
 39  	ast_set_start_address_crt1(ast, fb->mmio_addr);
 40  
 41  	return 0;
 42  }
 43  
 44  static void ast_edid_to_drmmode(struct edid *edid, struct drm_display_mode *mode)
 45  {
 46  	memset(mode, 0, sizeof(*mode));
 47  
 48  	mode->hdisplay = edid->mode.ha;
 49  	mode->vdisplay = edid->mode.va;
 50  	mode->crtc_hdisplay = edid->mode.ha;
 51  	mode->crtc_vdisplay = edid->mode.va;
 52  
 53  	/* EDID clock is in 10kHz, but drm clock is in KHz */
 54  	mode->clock = edid->mode.pixel_clock * 10;
 55  	mode->vrefresh = edid->mode.refresh;
 56  
 57  	mode->crtc_hblank_start = edid->mode.ha;
 58  	mode->crtc_hblank_end = edid->mode.ha + edid->mode.hbl;
 59  	mode->crtc_hsync_start = edid->mode.ha + edid->mode.hso;
 60  	mode->crtc_hsync_end = edid->mode.ha + edid->mode.hso + edid->mode.hspw;
 61  	mode->crtc_htotal = mode->crtc_hblank_end;
 62  
 63  	mode->crtc_vblank_start = edid->mode.va;
 64  	mode->crtc_vblank_end = edid->mode.va + edid->mode.vbl;
 65  	mode->crtc_vsync_start = edid->mode.va + edid->mode.vso;
 66  	mode->crtc_vsync_end = edid->mode.va + edid->mode.vso + edid->mode.vspw;
 67  	mode->crtc_vtotal = mode->crtc_vblank_end;
 68  
 69  	mode->flags = 0;
 70  	if (edid->mode.phsync == '+')
 71  		mode->flags |= DRM_MODE_FLAG_PHSYNC;
 72  	else
 73  		mode->flags |= DRM_MODE_FLAG_NHSYNC;
 74  
 75  	if (edid->mode.pvsync == '+')
 76  		mode->flags |= DRM_MODE_FLAG_PVSYNC;
 77  	else
 78  		mode->flags |= DRM_MODE_FLAG_NVSYNC;
 79  }
 80  
 81  static int ast_select_mode(struct drm_connector *connector,
 82  			   struct edid *edid)
 83  {
 84  	struct ast_private *ast = connector->dev->dev_private;
 85  	bool widescreen;
 86  	u8 raw[128];
 87  	bool flags = false;
 88  
 89  	if (ast->tx_chip_type == AST_TX_DP501) {
 90  		ast->dp501_maxclk = 0xff;
 91  		flags = ast_dp501_read_edid(connector->dev, (u8 *)raw);
 92  		if (flags)
 93  			ast->dp501_maxclk = ast_get_dp501_max_clk(connector->dev);
 94  		else
 95  			dev_err(dev->pdev, "I2C transmission error\n");
 96  	}
 97  
 98  	if (!flags)
 99  		ast_software_i2c_read(ast, raw);
100  
101  	if (decode_edid(raw, sizeof(raw), edid) != EDID_CONFORMANT) {
102  		/*
103  		 * Servers often run headless, so a missing EDID is not an error.
104  		 * We still need to initialize a framebuffer for KVM, though.
105  		 */
106  		dev_info(dev->pdev, "Failed to decode EDID\n");
107  		printk(BIOS_DEBUG, "Assuming VGA for KVM\n");
108  
109  		memset(edid, 0, sizeof(*edid));
110  
111  		edid->mode.pixel_clock = 6411;
112  		edid->mode.refresh = 60;
113  		edid->mode.ha = 1024;
114  		edid->mode.hspw = 4;
115  		edid->mode.hso = 56;
116  		edid->mode.hbl = 264;
117  		edid->mode.phsync = '-';
118  
119  		edid->mode.va = 768;
120  		edid->mode.vspw = 3;
121  		edid->mode.vso = 1;
122  		edid->mode.vbl = 26;
123  		edid->mode.pvsync = '+';
124  	}
125  
126  	printk(BIOS_DEBUG, "AST: Display has %dpx x %dpx\n", edid->mode.ha, edid->mode.va);
127  
128  	widescreen = !!(((edid->mode.ha * 4) % (edid->mode.va * 3)));
129  
130  	while (ast_mode_valid(connector, edid->mode.ha, edid->mode.va) != MODE_OK) {
131  		/* Select a compatible smaller mode */
132  		if (edid->mode.ha > 1920 && widescreen) {
133  			edid->mode.ha = 1920;
134  			edid->mode.va = 1080;
135  		} else if (edid->mode.ha >= 1920 && widescreen) {
136  			edid->mode.ha = 1680;
137  			edid->mode.va = 1050;
138  		} else if (edid->mode.ha >= 1680 && widescreen) {
139  			edid->mode.ha = 1600;
140  			edid->mode.va = 900;
141  		} else if (edid->mode.ha >= 1680 && !widescreen) {
142  			edid->mode.ha = 1600;
143  			edid->mode.va = 1200;
144  		} else if (edid->mode.ha >= 1600 && widescreen) {
145  			edid->mode.ha = 1440;
146  			edid->mode.va = 900;
147  		} else if (edid->mode.ha >= 1440 && widescreen) {
148  			edid->mode.ha = 1360;
149  			edid->mode.va = 768;
150  		} else if (edid->mode.ha >= 1360 && widescreen) {
151  			edid->mode.ha = 1280;
152  			edid->mode.va = 800;
153  		} else if (edid->mode.ha >= 1360 && !widescreen) {
154  			edid->mode.ha = 1280;
155  			edid->mode.va = 1024;
156  		} else if (edid->mode.ha >= 1280) {
157  			edid->mode.ha = 1024;
158  			edid->mode.va = 768;
159  		} else if (edid->mode.ha >= 1024) {
160  			edid->mode.ha = 800;
161  			edid->mode.va = 600;
162  		} else if (edid->mode.ha >= 800) {
163  			edid->mode.ha = 640;
164  			edid->mode.va = 480;
165  		} else {
166  			dev_err(dev->pdev, "No compatible mode found.\n");
167  
168  			return -EIO;
169  		}
170  	};
171  
172  	return 0;
173  }
174  
175  int ast_driver_framebuffer_init(struct drm_device *dev, int flags)
176  {
177  	struct drm_display_mode adjusted_mode;
178  	struct drm_crtc crtc;
179  	struct drm_format format;
180  	struct drm_primary primary;
181  	struct drm_framebuffer fb;
182  	struct drm_connector connector;
183  	struct edid edid;
184  	int ret;
185  
186  	/* Init wrapper structs */
187  	connector.dev = dev;
188  
189  	format.cpp[0] = 4; /* 32 BPP */
190  	fb.format = &format;
191  
192  	primary.fb = &fb;
193  
194  	crtc.dev = dev;
195  	crtc.primary = &primary;
196  
197  	/* Read EDID and find mode */
198  	ret = ast_select_mode(&connector, &edid);
199  	if (ret) {
200  		dev_err(dev->pdev, "Failed to select mode.\n");
201  		return ret;
202  	}
203  
204  	/* Updated edid for fb_fill_framebuffer_info */
205  	edid.x_resolution = edid.mode.ha;
206  	edid.y_resolution = edid.mode.va;
207  	edid.framebuffer_bits_per_pixel = format.cpp[0] * 8;
208  	edid.bytes_per_line = ALIGN_UP(edid.x_resolution * format.cpp[0], 8);
209  
210  	/* Updated framebuffer info for ast_crtc_mode_set */
211  	fb.pitches[0] = edid.bytes_per_line;
212  
213  	printk(BIOS_DEBUG, "Using framebuffer %dpx x %dpx pitch %d @ %d BPP\n",
214  	       edid.x_resolution, edid.y_resolution, edid.bytes_per_line,
215  	       edid.framebuffer_bits_per_pixel);
216  
217  	/* Convert EDID to AST DRM mode */
218  	ast_edid_to_drmmode(&edid, &crtc.mode);
219  
220  	memcpy(&adjusted_mode, &crtc.mode, sizeof(crtc.mode));
221  
222  	ret = ast_crtc_mode_set(&crtc, &crtc.mode, &adjusted_mode);
223  	if (ret) {
224  		dev_err(dev->pdev, "Failed to set mode.\n");
225  		return ret;
226  	}
227  
228  	ast_hide_cursor(&crtc);
229  
230  	/* Advertise new mode */
231  	fb_new_framebuffer_info_from_edid(&edid, fb.mmio_addr);
232  
233  	/* Clear display */
234  	memset((void *)(uintptr_t)fb.mmio_addr, 0, edid.bytes_per_line * edid.y_resolution);
235  
236  	return 0;
237  }