Commit 8b856f040c09024aa9d1f363c1a5cf2d3db73ebd
Committed by
Grant Likely
1 parent
a027b33348
Exists in
master
and in
4 other branches
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
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); |