Support duo4k.
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / Videomode / VideoHardware.py
1 from Components.config import config, ConfigSubDict, ConfigSelection, ConfigNothing, NoSave
2 from Tools.CList import CList
3 from Tools.HardwareInfo import HardwareInfo
4 import os
5
6 # VideoHardware is the interface to /proc/stb/video.
7 class VideoHardware:
8         is_init = True
9
10         modes = { # a list of modes for available port
11                 "Scart" : ["PAL", "NTSC", "Multi"],
12                 "YPbPr" : ["720p", "1080i", "576p", "480p", "576i", "480i"],
13                 "DVI"   : ["720p", "1080i", "576p", "480p", "576i", "480i"],
14                 "DVI-PC": ["PC"]
15         }
16         rates = { # list of rates for available mode
17                 "PAL":   { "50Hz" : {50: "pal"},
18                                    "60Hz" : {60: "pal60"},
19                                    "multi": {50: "pal", 60: "pal60"}
20                 },
21                 "NTSC":  { "60Hz" : {60: "ntsc"} },
22                 "Multi": { "multi": {50: "pal", 60: "ntsc"} },
23                 "480i":  { "60Hz" : {60: "480i"} },
24                 "576i":  { "50Hz" : {50: "576i"} },
25                 "480p":  { "60Hz" : {60: "480p"} },
26                 "576p":  { "50Hz" : {50: "576p"} },
27                 "720p":  {
28                         "50Hz" : {50: "720p50"},
29                         "60Hz" : {60: "720p"},
30                         "multi": {50: "720p50", 60: "720p"}
31                 },
32                 "1080i": {
33                         "50Hz" : {50: "1080i50"},
34                         "60Hz" : {60: "1080i"},
35                         "multi": {50: "1080i50", 60: "1080i"}
36                 },
37                 "1080p": {
38                         "50Hz" : {50: "1080p50"},
39                         "60Hz" : {60: "1080p"},
40                         "multi": {50: "1080p50", 60: "1080p"}
41                 },
42                 "2160p": {
43                         "50Hz" : {50: "2160p50"},
44                         "60Hz" : {60: "2160p"},
45                         "multi": {50: "2160p50", 60: "2160p"}
46                 },
47                 "PC": {
48                         "1024x768": {60: "1024x768"},
49                         "800x600" : {60: "800x600"},
50                         "720x480" : {60: "720x480"},
51                         "720x576" : {60: "720x576"},
52                         "1280x720": {60: "1280x720"},
53                         "1280x720 multi": {50: "1280x720_50", 60: "1280x720"},
54                         "1920x1080": {60: "1920x1080"},
55                         "1920x1080 multi": {50: "1920x1080", 60: "1920x1080_50"},
56                         "1280x1024": {60: "1280x1024"},
57                         "1366x768": {60: "1366x768"},
58                         "1366x768 multi": {50: "1366x768", 60: "1366x768_50"},
59                         "1280x768": {60: "1280x768"},
60                         "640x480" : {60: "640x480"}
61                 }
62         }
63
64         widescreen_modes = set(["720p", "1080i", "1080p", "2160p"])
65         hdmi_hw_types = set(["dm500", "dm800se", "dm7020hd", "bm750", "solo", "uno", "ultimo", "solo2", "duo2", "solose", "zero", "solo4k", "ultimo4k", "uno4k", "uno4kse", "zero4k", "duo4k"])
66         hdmi_pc_hw_types = set(["dm500", "dm800se", "dm7020hd", "bm750", "solo", "uno", "ultimo", "solo2", "duo2", "solose", "zero", "solo4k", "ultimo4k", "uno4k", "uno4kse", "zero4k", "duo4k"])
67         noscart_hw_types = set(["zero", "solo4k", "ultimo4k", "uno4k", "uno4kse", "zero4k", "duo4k"])
68         noypbpr_hw_types = set(["solose", "zero", "solo4k", "ultimo4k", "uno4k", "uno4kse", "zero4k", "duo4k"])
69
70         def getDeviceName(self):
71                 device_name = "unknown"
72                 try:
73                         file = open("/proc/stb/info/vumodel", "r")
74                         device_name = file.readline().strip()
75                         file.close()
76                 except IOError:
77                         from Tools.HardwareInfo import HardwareInfo
78                         device_name = HardwareInfo.get_device_name()
79
80                 return device_name
81
82         def isVumodel(self, hw_type):
83                 return hw_type in set(["bm750", "solo", "uno", "ultimo", "solo2", "duo2", "solose", "zero", "solo4k", "ultimo4k", "uno4k", "uno4kse", "zero4k", "duo4k"])
84
85         def isVumodel4K(self, hw_type):
86                 return hw_type in set(["solo4k", "ultimo4k", "uno4k", "uno4kse", "zero4k", "duo4k"])
87
88         # re-define AVSwitch.getOutputAspect
89         def getOutputAspect(self):
90                 ret = (16,9)
91                 port = config.av.videoport.value
92                 if port not in config.av.videomode:
93                         print "current port is not available. force 16:9"
94                 else:
95                         mode = config.av.videomode[port].value
96                         force_wide = self.isWidescreenMode(mode)
97                         valstr = config.av.aspect.value
98
99                         if force_wide:
100                                 pass
101                         elif valstr == "16_10":
102                                 ret = (16,10)
103                         elif valstr == "auto":
104                                 try:
105                                         aspect_str = open("/proc/stb/vmpeg/0/aspect", "r").read()
106                                         if aspect_str == "1": # 4:3
107                                                 ret = (4,3)
108                                 except IOError:
109                                         pass
110                         elif valstr == "4_3":
111                                 ret = (4,3)
112                 return ret
113
114         def __init__(self):
115                 self.last_modes_preferred = [ ]
116                 self.on_hotplug = CList()
117
118                 self.readAvailableModes()
119
120                 if self.modes.has_key("DVI-PC") and not self.getModeList("DVI-PC"):
121                         print "remove DVI-PC because it does not exist."
122                         del self.modes["DVI-PC"]
123
124                 if self.isNoScart(self.getDeviceName()):
125                         if self.modes.has_key("Scart"):
126                                 print "remove Scart because it does not exist."
127                                 del self.modes["Scart"]
128
129                 if self.isNoYPbPr(self.getDeviceName()):
130                         if self.modes.has_key("YPbPr"):
131                                 print "remove YPbPr because it does not exist."
132                                 del self.modes["YPbPr"]
133
134                 self.createConfig()
135
136                 self.readPreferredModes()
137
138                 # re-define AVSwitch components
139                 from Components.AVSwitch import AVSwitch
140                 config.av.aspectratio.notifiers = [ ]
141                 config.av.tvsystem.notifiers = [ ]
142                 config.av.wss.notifiers = [ ]
143                 AVSwitch.getOutputAspect = self.getOutputAspect
144
145                 config.av.aspect.addNotifier(self.changedAspect)
146                 config.av.wss.addNotifier(self.changedAspect)
147                 config.av.policy_169.addNotifier(self.changedAspect)
148                 config.av.policy_43.addNotifier(self.changedAspect)
149
150                 # addNotifiers for port, mode, rate
151                 config.av.videoport.addNotifier(self.changedVideomode)
152                 for port in self.getPortList():
153                         config.av.videomode[port].addNotifier(self.changedVideomode)
154                         for mode in self.getModeList(port):
155                                 config.av.videorate[mode[0]].addNotifier(self.changedVideomode)
156
157                 self.is_init = False
158
159         def readAvailableModes(self):
160                 try:
161                         modes = open("/proc/stb/video/videomode_choices").read()[:-1]
162                         self.modes_available = modes.split(' ')
163                 except IOError:
164                         print "failed to read video_choices."
165                         self.modes_available = [ ]
166
167         def readPreferredModes(self):
168                 try:
169                         modes = open("/proc/stb/video/videomode_preferred").read()[:-1]
170                         self.modes_preferred = modes.split(' ')
171                 except IOError:
172                         print "failed to read video_preferred."
173                         self.modes_preferred = self.modes_available
174
175                 if self.modes_preferred != self.last_modes_preferred:
176                         self.last_modes_preferred = self.modes_preferred
177                         print "hotplug on DVI"
178                         self.on_hotplug("DVI") # must be DVI
179
180         # check if HDMI is available
181         def isHDMIAvailable(self, hw_type):
182                 return hw_type in self.hdmi_hw_types
183
184         # check if HDMI-PC is available
185         def isHDMI_PCAvailable(self, hw_type):
186                 return hw_type in self.hdmi_pc_hw_types
187
188         # check if mode is always widescreen
189         def isWidescreenMode(self, mode):
190                 return mode in self.widescreen_modes
191
192         # check if Scart is not available
193         def isNoScart(self, hw_type):
194                 return hw_type in self.noscart_hw_types
195
196         # check if YPbPr is not available
197         def isNoYPbPr(self, hw_type):
198                 return hw_type in self.noypbpr_hw_types
199
200         # check if rate is available for mode
201         def isModeAvailable(self, port, mode, rate):
202                 rate = self.rates[mode][rate]
203                 for mode in rate.values():
204                         if mode not in self.modes_available:
205                                 return False
206
207                 return True
208
209         # check isModeAvailable in this port
210         def isPortAvailable(self, port):
211                 for mode in self.getModeList(port):
212                         if len(self.getRateList(port, mode[0])):
213                                 return True
214
215                 return False
216
217         # get a list of all available port
218         def getPortList(self):
219                 return [port for port in self.modes if self.isPortAvailable(port)]
220
221         # get a list of all available mode for a given port
222         def getModeList(self, port):
223                 modelist = [ ]
224                 for mode in self.modes[port]:
225                         rates = self.getRateList(port, mode)
226
227                         if len(rates):
228                                 modelist.append( (mode, rates))
229
230                 return modelist
231
232         # get a list of all available rate for a given port, mode
233         def getRateList(self, port, mode):
234                 return [rate for rate in self.rates[mode] if self.isModeAvailable(port, mode, rate)]
235
236         def createConfig(self):
237                 config.av.videomode = ConfigSubDict()
238                 config.av.videorate = ConfigSubDict()
239
240                 hw_type = self.getDeviceName()
241                 # vu+ support 1080p
242                 if self.isVumodel(hw_type):
243                         self.modes["DVI"].insert(self.modes["DVI"].index("1080i")+1, "1080p")
244                         # 4K support 2160p
245                         if self.isVumodel4K(hw_type):
246                                 self.modes["DVI"].insert(self.modes["DVI"].index("1080p")+1, "2160p")
247
248                 portlist = [ ]
249                 port_choices = self.getPortList()
250
251                 for port in port_choices:
252                         desc = port
253                         if desc == 'DVI' and self.isHDMIAvailable(hw_type):
254                                 desc = 'HDMI'
255                         if desc == 'DVI-PC' and self.isHDMI_PCAvailable(hw_type):
256                                 desc = 'HDMI-PC'
257                         portlist.append( (port, desc))
258
259                         # create list of available modes
260                         modelist = [ ]
261                         mode_choices = self.getModeList(port)
262
263                         for mode in mode_choices:
264                                 modelist.append( (mode[0], mode[0]))
265
266                                 # create list of available rates
267                                 ratelist = [ ]
268                                 rate_choices = self.getRateList(port, mode[0])
269
270                                 for rate in rate_choices:
271                                         ratelist.append( (rate, rate))
272
273                                 config.av.videorate[mode[0]] = ConfigSelection(choices = ratelist)
274                         config.av.videomode[port] = ConfigSelection(choices = modelist)
275                 config.av.videoport = ConfigSelection(choices = portlist)
276
277                 if os.path.exists("/proc/stb/video/hdmi_colorspace"):
278                         def setHdmiColorspace(config):
279                                 try:
280                                         print "set HDMI Colorspace : ",config.value
281                                         f = open("/proc/stb/video/hdmi_colorspace", "w")
282                                         f.write(config.value)
283                                         f.close()
284                                 except IOError:
285                                         print "set HDMI Colorspace failed!"
286                         hdmicolorspace_choices = {}
287                         hdmicolorspace_choices["Edid(Auto)"] = _("Auto")
288                         hdmicolorspace_choices["Hdmi_Rgb"] = _("RGB")
289                         hdmicolorspace_choices["444"] = _("YCbCr444")
290                         hdmicolorspace_choices["422"] = _("YCbCr422")
291                         hdmicolorspace_choices["420"] = _("YCbCr420")
292                         config.av.hdmicolorspace = ConfigSelection(choices = hdmicolorspace_choices, default = "Edid(Auto)")
293                         config.av.hdmicolorspace.addNotifier(setHdmiColorspace)
294                 else:
295                         config.av.hdmicolorspace = NoSave(ConfigNothing())
296
297                 if os.path.exists("/proc/stb/video/hdmi_colordepth"):
298                         def setHdmiColordepth(config):
299                                 try:
300                                         print "set HDMI Colordepth : ",config.value
301                                         f = open("/proc/stb/video/hdmi_colordepth", "w")
302                                         f.write(config.value)
303                                         f.close()
304                                 except IOError:
305                                         print "set HDMI Colordepth failed!"
306                         hdmicolordepth_choices = {}
307                         hdmicolordepth_choices["auto"] = _("Auto")
308                         hdmicolordepth_choices["8bit"] = _("8bit")
309                         hdmicolordepth_choices["10bit"] = _("10bit")
310                         hdmicolordepth_choices["12bit"] = _("12bit")
311                         config.av.hdmicolordepth = ConfigSelection(choices = hdmicolordepth_choices, default = "auto")
312                         config.av.hdmicolordepth.addNotifier(setHdmiColordepth)
313                 else:
314                         config.av.hdmicolordepth = NoSave(ConfigNothing())
315
316         def changedVideomode(self, configElement):
317                 if self.is_init:
318                         return
319
320                 self.setConfiguredMode()
321
322         def setConfiguredMode(self):
323                 port = config.av.videoport.value
324                 mode = config.av.videomode[port].value
325                 rate = config.av.videorate[mode].value
326
327                 self.setVideomode(port, mode, rate)
328
329         def setVideomode(self, port, mode, rate):
330                 if port is None or port not in config.av.videomode:
331                         print "current port not available. couldn't set videomode"
332                         return
333
334                 if mode not in config.av.videorate:
335                         print "current mode not available. couldn't set videomode"
336                         return
337
338                 if mode is None:
339                         modelist = self.getModeList(port)
340                         mode = modelist[0][0]
341
342                         ratelist = self.getRateList(port, mode)
343                         rate = ratelist[0]
344
345                 if rate is None:
346                         ratelist = self.getRateList(port, mode)
347                         rate = ratelist[0]
348
349                 print "set Videomode", port, mode, rate
350
351                 modes = self.rates[mode][rate]
352                 mode_50 = modes.get(50)
353                 mode_60 = modes.get(60)
354                 if mode_50 is None:
355                         mode_50 = mode_60
356                 if mode_60 is None:
357                         mode_60 = mode_50
358
359                 if (mode_50 != mode_60):
360                         try:
361                                 open("/proc/stb/video/videomode_50hz", "w").write(mode_50)
362                                 open("/proc/stb/video/videomode_60hz", "w").write(mode_60)
363                         except IOError:
364                                 print "cannot open /proc/stb/vide/videomode_50hz or videomode_60hz"
365
366                         # Too slow moving to Scart/multi in modeSelectionMoved
367                         #try:
368                         #       open("/proc/stb/video/videomode_50hz", "w").write(mode_60)
369                         #except IOError:
370                         #       print "cannot open /proc/stb/vide/videomode_60Hz"
371
372                 else:
373                         try:
374                                 open("/proc/stb/video/videomode", "w").write(mode_50)
375                         except IOError:
376                                 print "cannot open /proc/stb/vide/videomode"
377
378                 self.changedAspect(None)
379
380         def changedAspect(self, configElement):
381                 if self.is_init:
382                         return
383                 # config.av.aspect:
384                 #       4:3                     use policy_169
385                 #       16:9, 16:10     use policy_43
386                 #       auto            always "bestfit"
387                 # config.av.policy_169:
388                 #       letterbox       use letterbox
389                 #       panscan         use panscan
390                 #       scale           use bestfit
391                 # config.av.policy_43:
392                 #       pillarbox       use panscan
393                 #       pansca          use letterbox ("panscan" is just a bad term, it is inverse-panscan)
394                 #       nonlinear       use nonlinear
395                 #       scale           use bestfit
396
397                 port = config.av.videoport.value
398                 if port not in config.av.videomode:
399                         print "current port not available. couldn't set aspect"
400                         return
401
402                 mode = config.av.videomode[port].value
403                 force_wide = self.isWidescreenMode(mode)
404                 valstr = config.av.aspect.value
405
406                 policy2 = "policy" # use main policy
407
408                 if force_wide or valstr == "16_9" or valstr == "16_10":
409                         if force_wide or valstr == "16_9":
410                                 aspect = "16:9"
411                         elif valstr == "16_10":
412                                 aspect = "16:10"
413
414                         policy = {"pillarbox": "panscan", "panscan": "letterbox", "nonlinear": "nonlinear", "scale": "bestfit"}[config.av.policy_43.value]
415                         policy2 = {"letterbox": "letterbox", "panscan": "panscan", "scale": "bestfit"}[config.av.policy_169.value]
416                 elif valstr == "auto":
417                         aspect = "any"
418                         policy = "bestfit"
419                 else:
420                         aspect = "4:3"
421                         policy = {"letterbox": "letterbox", "panscan": "panscan", "scale": "bestfit"}[config.av.policy_169.value]
422
423                 if not config.av.wss.value:
424                         wss = "auto(4:3_off)"
425                 else:
426                         wss = "auto"
427
428                 self.setAspect(aspect, policy, policy2, wss)
429
430         def setAspect(self, aspect, policy, policy2, wss):
431                 print "set aspect, policy, policy2, wss", aspect, policy, policy2, wss
432
433                 open("/proc/stb/video/aspect", "w").write(aspect)
434                 open("/proc/stb/video/policy", "w").write(policy)
435                 open("/proc/stb/denc/0/wss", "w").write(wss)
436                 try:
437                         open("/proc/stb/video/policy2", "w").write(policy2)
438                 except IOError:
439                         pass
440
441         def isPortUsed(self, port):
442                 if port == "DVI":
443                         self.readPreferredModes()
444                         return len(self.modes_preferred) != 0
445                 else:
446                         return True
447
448         def saveVideomode(self, port, mode, rate):
449                 print "save Videomode", port, mode, rate
450                 config.av.videoport.value = port
451                 config.av.videoport.save()
452                 if port in config.av.videomode:
453                         config.av.videomode[port].value = mode
454                         config.av.videomode[port].save()
455                 if mode in config.av.videorate:
456                         config.av.videorate[mode].value = rate
457                         config.av.videorate[mode].save()
458
459         # for dependency
460         def setMode(self, port, mode, rate):
461                 self.setVideomode(port, mode, rate)
462
463         def saveMode(self, port, mode, rate):
464                 self.saveVideomode(port, mode, rate)
465
466         def updateAspect(self, configElement):
467                 self.changedAspect(configElement)
468
469 video_hw = VideoHardware()
470 video_hw.setConfiguredMode()
471