Commit 8b856f040c09024aa9d1f363c1a5cf2d3db73ebd

Authored by Anatolij Gustschin
Committed by Grant Likely
1 parent a027b33348

powerpc/fsl-diu-fb: Support setting display mode using EDID

Adds support for encoding display mode information
in the device tree using verbatim EDID block.

If the EDID entry in the DIU node is present, the
driver will build mode database using EDID data
and allow setting the display modes from this database.
Otherwise display mode will be set using mode
entries from driver's internal database as usual.

This patch also updates device tree bindings.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Acked-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

Showing 3 changed files with 81 additions and 6 deletions Side-by-side Diff

Documentation/powerpc/dts-bindings/fsl/diu.txt
... ... @@ -11,6 +11,11 @@
11 11 - interrupt-parent : the phandle for the interrupt controller that
12 12 services interrupts for this device.
13 13  
  14 +Optional properties:
  15 +- edid : verbatim EDID data block describing attached display.
  16 + Data from the detailed timing descriptor will be used to
  17 + program the display controller.
  18 +
14 19 Example (MPC8610HPCD):
15 20 display@2c000 {
16 21 compatible = "fsl,diu";
... ... @@ -25,5 +30,6 @@
25 30 reg = <0x2100 0x100>;
26 31 interrupts = <64 0x8>;
27 32 interrupt-parent = <&ipic>;
  33 + edid = [edid-data];
28 34 };
drivers/video/Kconfig
... ... @@ -1871,6 +1871,7 @@
1871 1871 config FB_FSL_DIU
1872 1872 tristate "Freescale DIU framebuffer support"
1873 1873 depends on FB && FSL_SOC
  1874 + select FB_MODE_HELPERS
1874 1875 select FB_CFB_FILLRECT
1875 1876 select FB_CFB_COPYAREA
1876 1877 select FB_CFB_IMAGEBLIT
drivers/video/fsl-diu-fb.c
... ... @@ -35,6 +35,7 @@
35 35  
36 36 #include <sysdev/fsl_soc.h>
37 37 #include <linux/fsl-diu-fb.h>
  38 +#include "edid.h"
38 39  
39 40 /*
40 41 * These parameters give default parameters
... ... @@ -217,6 +218,7 @@
217 218 int x_aoi_d; /* aoi display x offset to physical screen */
218 219 int y_aoi_d; /* aoi display y offset to physical screen */
219 220 struct fsl_diu_data *parent;
  221 + u8 *edid_data;
220 222 };
221 223  
222 224  
223 225  
224 226  
225 227  
226 228  
... ... @@ -1185,18 +1187,30 @@
1185 1187 int rc;
1186 1188 struct mfb_info *mfbi = info->par;
1187 1189 const char *aoi_mode, *init_aoi_mode = "320x240";
  1190 + struct fb_videomode *db = fsl_diu_mode_db;
  1191 + unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
  1192 + int has_default_mode = 1;
1188 1193  
1189 1194 if (init_fbinfo(info))
1190 1195 return -EINVAL;
1191 1196  
1192   - if (mfbi->index == 0) /* plane 0 */
  1197 + if (mfbi->index == 0) { /* plane 0 */
  1198 + if (mfbi->edid_data) {
  1199 + /* Now build modedb from EDID */
  1200 + fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs);
  1201 + fb_videomode_to_modelist(info->monspecs.modedb,
  1202 + info->monspecs.modedb_len,
  1203 + &info->modelist);
  1204 + db = info->monspecs.modedb;
  1205 + dbsize = info->monspecs.modedb_len;
  1206 + }
1193 1207 aoi_mode = fb_mode;
1194   - else
  1208 + } else {
1195 1209 aoi_mode = init_aoi_mode;
  1210 + }
1196 1211 pr_debug("mode used = %s\n", aoi_mode);
1197   - rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
1198   - ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mode, default_bpp);
1199   -
  1212 + rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize,
  1213 + &fsl_diu_default_mode, default_bpp);
1200 1214 switch (rc) {
1201 1215 case 1:
1202 1216 pr_debug("using mode specified in @mode\n");
1203 1217  
... ... @@ -1214,10 +1228,50 @@
1214 1228 default:
1215 1229 pr_debug("rc = %d\n", rc);
1216 1230 pr_debug("failed to find mode\n");
1217   - return -EINVAL;
  1231 + /*
  1232 + * For plane 0 we continue and look into
  1233 + * driver's internal modedb.
  1234 + */
  1235 + if (mfbi->index == 0 && mfbi->edid_data)
  1236 + has_default_mode = 0;
  1237 + else
  1238 + return -EINVAL;
1218 1239 break;
1219 1240 }
1220 1241  
  1242 + if (!has_default_mode) {
  1243 + rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
  1244 + ARRAY_SIZE(fsl_diu_mode_db),
  1245 + &fsl_diu_default_mode,
  1246 + default_bpp);
  1247 + if (rc > 0 && rc < 5)
  1248 + has_default_mode = 1;
  1249 + }
  1250 +
  1251 + /* Still not found, use preferred mode from database if any */
  1252 + if (!has_default_mode && info->monspecs.modedb) {
  1253 + struct fb_monspecs *specs = &info->monspecs;
  1254 + struct fb_videomode *modedb = &specs->modedb[0];
  1255 +
  1256 + /*
  1257 + * Get preferred timing. If not found,
  1258 + * first mode in database will be used.
  1259 + */
  1260 + if (specs->misc & FB_MISC_1ST_DETAIL) {
  1261 + int i;
  1262 +
  1263 + for (i = 0; i < specs->modedb_len; i++) {
  1264 + if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
  1265 + modedb = &specs->modedb[i];
  1266 + break;
  1267 + }
  1268 + }
  1269 + }
  1270 +
  1271 + info->var.bits_per_pixel = default_bpp;
  1272 + fb_videomode_to_var(&info->var, modedb);
  1273 + }
  1274 +
1221 1275 pr_debug("xres_virtual %d\n", info->var.xres_virtual);
1222 1276 pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel);
1223 1277  
... ... @@ -1256,6 +1310,9 @@
1256 1310 if (!mfbi->registered)
1257 1311 return;
1258 1312  
  1313 + if (mfbi->index == 0)
  1314 + kfree(mfbi->edid_data);
  1315 +
1259 1316 unregister_framebuffer(info);
1260 1317 unmap_video_memory(info);
1261 1318 if (&info->cmap)
... ... @@ -1456,6 +1513,17 @@
1456 1513 mfbi = machine_data->fsl_diu_info[i]->par;
1457 1514 memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
1458 1515 mfbi->parent = machine_data;
  1516 +
  1517 + if (mfbi->index == 0) {
  1518 + const u8 *prop;
  1519 + int len;
  1520 +
  1521 + /* Get EDID */
  1522 + prop = of_get_property(np, "edid", &len);
  1523 + if (prop && len == EDID_LENGTH)
  1524 + mfbi->edid_data = kmemdup(prop, EDID_LENGTH,
  1525 + GFP_KERNEL);
  1526 + }
1459 1527 }
1460 1528  
1461 1529 ret = of_address_to_resource(np, 0, &res);