├── .travis.yml ├── LICENSE ├── README.md ├── debian ├── changelog ├── compat ├── control ├── copyright ├── gbp.conf ├── golang-sunxi-disp-tool-dev.install ├── rules ├── source │ └── format ├── sunxi-disp-tool.install └── sunxi-disp-tool.service ├── disp.go ├── disp2 ├── disp2.go ├── ioctl.go ├── output_type.go └── tv_mode.go ├── fb ├── fb.go └── fbset.go ├── kernel └── cmdline.go └── sunxi-disp-tool └── main.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | - 1.6 9 | - 1.7 10 | - 1.8 11 | - tip 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Simon Eisenmann. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of the copyright holder, nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sunxi disp tool 2 | 3 | This utility allows to control a sunxi disp2 device from user space. The code 4 | is organized in Go modules, so it can easily be integrated. 5 | 6 | ## Usage 7 | 8 | Make sure to stop any graphical UI before running this command to make sure 9 | that the existing framebuffer can be properly freed and reassigned. Failing 10 | this will result in wrong framebuffer size and thus garbled display. 11 | 12 | ``` 13 | Usage: sunxi-disp-tool [] [] 14 | Global options: 15 | -screen int 16 | Screen ID (default 0) 17 | Available commands are: 18 | switch Switch HDMI output mode 19 | init Initialize HDMI output mode from Kernel args 20 | 21 | ``` 22 | 23 | ### Examples 24 | 25 | ```bash 26 | sunxi-disp-tool switch -mode 0xa 27 | sunxi-disp-tool switch -mode 0x5 28 | sunxi-disp-tool switch -name 720p 29 | sunxi-disp-tool switch -name 1080p 30 | sunxi-disp-tool switch -name 1020x720p60 31 | ``` 32 | 33 | ### Run automatically on boot 34 | 35 | On boards like the Pine64 with `uEnv.txt` support simply add a line to provide 36 | additional Kernel arguments. 37 | 38 | ```bash 39 | cat < Sun, 30 Apr 2017 22:35:24 +0200 6 | 7 | sunxi-disp-tool (0.0.4) xenial; urgency=medium 8 | 9 | * Fix init command so it actually works. 10 | 11 | -- Simon Eisenmann Fri, 06 May 2016 21:49:53 +0200 12 | 13 | sunxi-disp-tool (0.0.2) xenial; urgency=medium 14 | 15 | * Ennable systemd service on install. 16 | 17 | -- Simon Eisenmann Fri, 06 May 2016 21:34:16 +0200 18 | 19 | sunxi-disp-tool (0.0.1) xenial; urgency=medium 20 | 21 | * Initial release. 22 | 23 | -- Simon Eisenmann Fri, 6 May 2016 20:28:24 +0200 24 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: sunxi-disp-tool 2 | Section: kernel 3 | Priority: optional 4 | Maintainer: Simon Eisenmann 5 | Build-Depends: debhelper (>= 9), 6 | dh-golang, 7 | golang-go (>= 1.2.0), 8 | dh-systemd 9 | Standards-Version: 3.9.7 10 | 11 | Package: golang-sunxi-disp-tool-dev 12 | Architecture: all 13 | Depends: ${misc:Depends} 14 | Description: Control Sunxi disp2 devices from Go 15 | Go development package to control Sunxi disp2 devices 16 | 17 | Package: sunxi-disp-tool 18 | Architecture: any 19 | Depends: 20 | ${shlibs:Depends}, 21 | ${misc:Depends} 22 | Description: Command line tool to control Sunxi disp2 devices 23 | This package provides a command line user space tool to change 24 | settings like resultion of Sunxi disp2 display devices 25 | 26 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | 3 | Files: * 4 | Copyright: 2016 Simon Eisenmann 5 | License: BSD-3-clause 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | . 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | . 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | . 17 | 3. Neither the name of the copyright holder nor, the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | . 21 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /debian/gbp.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | debian-branch = master 3 | debian-tag = ubuntu/xenial/%(version)s 4 | 5 | -------------------------------------------------------------------------------- /debian/golang-sunxi-disp-tool-dev.install: -------------------------------------------------------------------------------- 1 | /usr/share/gocode 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | #export DH_VERBOSE=1 4 | export DH_OPTIONS 5 | export DH_GOPKG := github.com/longsleep/sunxi-disp-tool 6 | 7 | %: 8 | dh $@ --buildsystem=golang --with=golang,systemd --fail-missing 9 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/sunxi-disp-tool.install: -------------------------------------------------------------------------------- 1 | /usr/bin/sunxi-disp-tool 2 | -------------------------------------------------------------------------------- /debian/sunxi-disp-tool.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Initialize Sunxi disp2 HDMI output mode 3 | DefaultDependencies=no 4 | Before=sysinit.target 5 | ConditionPathIsReadWrite=/sys 6 | ConditionPathExists=/dev/disp 7 | 8 | [Service] 9 | ExecStart=/usr/bin/sunxi-disp-tool init 10 | Type=oneshot 11 | RemainAfterExit=yes 12 | 13 | [Install] 14 | WantedBy=sysinit.target 15 | -------------------------------------------------------------------------------- /disp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package disp 6 | 7 | import ( 8 | _ "github.com/longsleep/sunxi-disp-tool/disp2" 9 | _ "github.com/longsleep/sunxi-disp-tool/fb" 10 | _ "github.com/longsleep/sunxi-disp-tool/kernel" 11 | ) 12 | -------------------------------------------------------------------------------- /disp2/disp2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | This implementation is based on information from the A64 BSP 1.2 Linux 7 | kernel source. 8 | 9 | The original disp2 framebuffer driver is fucked up and needs a patch like 10 | https://github.com/jernejsk/OpenELEC-OPi2/blob/openelec-7.0/projects/H3/patches/linux/linux-79-fbdev-fixes.patch 11 | to properly display after resolution change. Thanks to Jernej Škrabec for 12 | pointing me to it. 13 | */ 14 | 15 | package disp2 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "syscall" 21 | "unsafe" 22 | 23 | "github.com/longsleep/sunxi-disp-tool/fb" 24 | ) 25 | 26 | const ( 27 | DispDev = "/dev/disp" 28 | FbDevPrefix = "/dev/fb" 29 | ) 30 | 31 | type ioArgs [4]uint64 32 | 33 | type Disp2 struct { 34 | f *os.File 35 | } 36 | 37 | func New() (*Disp2, error) { 38 | f, err := os.OpenFile(DispDev, os.O_RDWR, 0) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | return &Disp2{ 44 | f: f, 45 | }, nil 46 | } 47 | 48 | func (disp *Disp2) Close() { 49 | disp.f.Close() 50 | } 51 | 52 | func (disp *Disp2) ioctl(cmd uint32, args *ioArgs) (uintptr, uintptr, syscall.Errno) { 53 | return syscall.Syscall(syscall.SYS_IOCTL, disp.f.Fd(), uintptr(cmd), uintptr(unsafe.Pointer(args))) 54 | } 55 | 56 | func (disp *Disp2) Switch(screen int, outputType uint64, outputMode uint64) error { 57 | currentOutputType, err := disp.GetOutputType(screen) 58 | if err != nil { 59 | return err 60 | } 61 | if currentOutputType != outputType { 62 | return fmt.Errorf("screen has wrong output type: %d", currentOutputType) 63 | } 64 | 65 | _, _, errno := disp.ioctl(DISP_DEVICE_SWITCH, &ioArgs{uint64(screen), outputType, outputMode}) 66 | if errno != 0 { 67 | return fmt.Errorf("ioctl switch failed %v", errno) 68 | } 69 | 70 | height, _ := disp.GetScnHeight(screen) 71 | width, _ := disp.GetScnWidth(screen) 72 | 73 | if width > 0 && height > 0 { 74 | // Set fb size as well. 75 | err := disp.fbSet(screen, width, height) 76 | if err != nil { 77 | return fmt.Errorf("fbset failed %v", err) 78 | } 79 | } else { 80 | return fmt.Errorf("width or height is zero") 81 | } 82 | 83 | disp.Blank(screen, false) 84 | 85 | return nil 86 | } 87 | 88 | func (disp *Disp2) Blank(screen int, blank bool) error { 89 | var b uint64 90 | if blank { 91 | b = 1 92 | } 93 | 94 | _, _, errno := disp.ioctl(DISP_BLANK, &ioArgs{uint64(screen), b}) 95 | if errno != 0 { 96 | return fmt.Errorf("ioctl blank failed %v", errno) 97 | } 98 | 99 | return nil 100 | } 101 | 102 | func (disp *Disp2) GetScnHeight(screen int) (uint32, error) { 103 | r1, _, errno := disp.ioctl(DISP_GET_SCN_HEIGHT, &ioArgs{uint64(screen)}) 104 | if errno != 0 { 105 | return 0, fmt.Errorf("ioctl get_scn_height failed %v", errno) 106 | } 107 | 108 | return uint32(r1), nil 109 | } 110 | 111 | func (disp *Disp2) GetScnWidth(screen int) (uint32, error) { 112 | r1, _, errno := disp.ioctl(DISP_GET_SCN_WIDTH, &ioArgs{uint64(screen)}) 113 | if errno != 0 { 114 | return 0, fmt.Errorf("ioctl get_scn_width failed %v", errno) 115 | } 116 | 117 | return uint32(r1), nil 118 | } 119 | 120 | func (disp *Disp2) GetOutputType(screen int) (uint64, error) { 121 | r1, _, errno := disp.ioctl(DISP_GET_OUTPUT_TYPE, &ioArgs{uint64(screen)}) 122 | if errno != 0 { 123 | return 0, fmt.Errorf("ioctl get_output_type failed %v", errno) 124 | } 125 | 126 | return uint64(r1), nil 127 | } 128 | 129 | func (disp *Disp2) fbSet(screen int, width, height uint32) error { 130 | set, err := fb.NewSet(fmt.Sprintf("%s%d", FbDevPrefix, screen)) 131 | if err != nil { 132 | return err 133 | } 134 | defer set.Close() 135 | 136 | info, err := set.GetVarScreenInfo() 137 | if err != nil { 138 | return err 139 | } 140 | 141 | info.SetXRes(width, width) 142 | info.SetYRes(height, height*2) 143 | 144 | return set.SetVarScreenInfo(info) 145 | } 146 | -------------------------------------------------------------------------------- /disp2/ioctl.go: -------------------------------------------------------------------------------- 1 | package disp2 2 | 3 | // All ioctl commands for sunxi disp2 i could find. Not all might be supported 4 | // with all Kernel trees out there. 5 | const ( 6 | DISP_RESERVE0 = 0x00 7 | DISP_RESERVE1 = 0x01 8 | DISP_SET_BKCOLOR = 0x03 9 | DISP_GET_BKCOLOR = 0x04 10 | DISP_SET_COLORKEY = 0x05 11 | DISP_GET_COLORKEY = 0x06 12 | DISP_GET_SCN_WIDTH = 0x07 13 | DISP_GET_SCN_HEIGHT = 0x08 14 | DISP_GET_OUTPUT_TYPE = 0x09 15 | DISP_SET_EXIT_MODE = 0x0A 16 | DISP_VSYNC_EVENT_EN = 0x0B 17 | DISP_BLANK = 0x0C 18 | DISP_SHADOW_PROTECT = 0x0D 19 | DISP_HWC_COMMIT = 0x0E 20 | DISP_DEVICE_SWITCH = 0x0F 21 | DISP_GET_OUTPUT = 0x10 22 | DISP_SET_COLOR_RANGE = 0x11 23 | DISP_GET_COLOR_RANGE = 0x12 24 | DISP_LAYER_ENABLE = 0x40 25 | DISP_LAYER_DISABLE = 0x41 26 | DISP_LAYER_SET_INFO = 0x42 27 | DISP_LAYER_GET_INFO = 0x43 28 | DISP_LAYER_TOP = 0x44 29 | DISP_LAYER_BOTTOM = 0x45 30 | DISP_LAYER_GET_FRAME_ID = 0x46 31 | DISP_LAYER_SET_CONFIG = 0x47 32 | DISP_LAYER_GET_CONFIG = 0x48 33 | DISP_HDMI_SUPPORT_MODE = 0xc4 34 | DISP_SET_TV_HPD = 0xc5 35 | DISP_HDMI_GET_EDID = 0xc6 36 | DISP_LCD_ENABLE = 0x100 37 | DISP_LCD_DISABLE = 0x101 38 | DISP_LCD_SET_BRIGHTNESS = 0x102 39 | DISP_LCD_GET_BRIGHTNESS = 0x103 40 | DISP_LCD_BACKLIGHT_ENABLE = 0x104 41 | DISP_LCD_BACKLIGHT_DISABLE = 0x105 42 | DISP_LCD_SET_SRC = 0x106 43 | DISP_LCD_SET_FPS = 0x107 44 | DISP_LCD_GET_FPS = 0x108 45 | DISP_LCD_GET_SIZE = 0x109 46 | DISP_LCD_GET_MODEL_NAME = 0x10a 47 | DISP_LCD_SET_GAMMA_TABLE = 0x10b 48 | DISP_LCD_GAMMA_CORRECTION_ENABLE = 0x10c 49 | DISP_LCD_GAMMA_CORRECTION_DISABLE = 0x10d 50 | DISP_LCD_USER_DEFINED_FUNC = 0x10e 51 | DISP_LCD_CHECK_OPEN_FINISH = 0x10f 52 | DISP_LCD_CHECK_CLOSE_FINISH = 0x110 53 | DISP_CAPTURE_START = 0x140 54 | DISP_CAPTURE_STOP = 0x141 55 | DISP_CAPTURE_COMMIT = 0x142 56 | DISP_ENHANCE_ENABLE = 0x180 57 | DISP_ENHANCE_DISABLE = 0x181 58 | DISP_ENHANCE_GET_EN = 0x182 59 | DISP_ENHANCE_SET_WINDOW = 0x183 60 | DISP_ENHANCE_GET_WINDOW = 0x184 61 | DISP_ENHANCE_SET_MODE = 0x185 62 | DISP_ENHANCE_GET_MODE = 0x186 63 | DISP_ENHANCE_DEMO_ENABLE = 0x187 64 | DISP_ENHANCE_DEMO_DISABLE = 0x188 65 | DISP_SMBL_ENABLE = 0x200 66 | DISP_SMBL_DISABLE = 0x201 67 | DISP_SMBL_GET_EN = 0x202 68 | DISP_SMBL_SET_WINDOW = 0x203 69 | DISP_SMBL_GET_WINDOW = 0x204 70 | DISP_FB_REQUEST = 0x280 71 | DISP_FB_RELEASE = 0x281 72 | DISP_MEM_REQUEST = 0x2c0 73 | DISP_MEM_RELEASE = 0x2c1 74 | DISP_MEM_GETADR = 0x2c2 75 | DISP_print = 0xffff 76 | ) 77 | -------------------------------------------------------------------------------- /disp2/output_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package disp2 6 | 7 | // All display output types i could find. Some might make no sense depended on 8 | // the board and the driver in the Kernel tree. 9 | const ( 10 | DISP_OUTPUT_TYPE_NONE = 0 11 | DISP_OUTPUT_TYPE_LCD = 1 12 | DISP_OUTPUT_TYPE_TV = 2 13 | DISP_OUTPUT_TYPE_HDMI = 4 14 | DISP_OUTPUT_TYPE_VGA = 8 15 | ) 16 | -------------------------------------------------------------------------------- /disp2/tv_mode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package disp2 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // All display output modes i could find. Actual support for those depend on 12 | // the chip and the driver in the Kernel tree in use. 13 | const ( 14 | DISP_TV_MOD_480I = 0 15 | DISP_TV_MOD_576I = 1 16 | DISP_TV_MOD_480P = 2 17 | DISP_TV_MOD_576P = 3 18 | DISP_TV_MOD_720P_50HZ = 4 19 | DISP_TV_MOD_720P_60HZ = 5 20 | DISP_TV_MOD_1080I_50HZ = 6 21 | DISP_TV_MOD_1080I_60HZ = 7 22 | DISP_TV_MOD_1080P_24HZ = 8 23 | DISP_TV_MOD_1080P_50HZ = 9 24 | DISP_TV_MOD_1080P_60HZ = 0xa 25 | DISP_TV_MOD_1080P_24HZ_3D_FP = 0x17 26 | DISP_TV_MOD_720P_50HZ_3D_FP = 0x18 27 | DISP_TV_MOD_720P_60HZ_3D_FP = 0x19 28 | DISP_TV_MOD_1080P_25HZ = 0x1a 29 | DISP_TV_MOD_1080P_30HZ = 0x1b 30 | DISP_TV_MOD_PAL = 0xb 31 | DISP_TV_MOD_PAL_SVIDEO = 0xc 32 | DISP_TV_MOD_NTSC = 0xe 33 | DISP_TV_MOD_NTSC_SVIDEO = 0xf 34 | DISP_TV_MOD_PAL_M = 0x11 35 | DISP_TV_MOD_PAL_M_SVIDEO = 0x12 36 | DISP_TV_MOD_PAL_NC = 0x14 37 | DISP_TV_MOD_PAL_NC_SVIDEO = 0x15 38 | DISP_TV_MOD_3840_2160P_30HZ = 0x1c 39 | DISP_TV_MOD_3840_2160P_25HZ = 0x1d 40 | DISP_TV_MOD_3840_2160P_24HZ = 0x1e 41 | DISP_TV_MODE_NUM = 0x1f 42 | 43 | DISP_TV_MODE_UNKOWN = 0xffff 44 | ) 45 | 46 | func GetTVModFromString(args ...string) int { 47 | for _, arg := range args { 48 | value := arg 49 | widthXheight := strings.SplitN(arg, "x", 2) 50 | if len(widthXheight) == 2 { 51 | value = widthXheight[1] 52 | } 53 | switch value { 54 | case "EDID": 55 | // Not implemented yet. 56 | case "720p50": 57 | return DISP_TV_MOD_720P_50HZ 58 | case "720p": 59 | fallthrough 60 | case "720p60": 61 | return DISP_TV_MOD_720P_60HZ 62 | case "1080i50": 63 | return DISP_TV_MOD_1080I_50HZ 64 | case "1080i": 65 | fallthrough 66 | case "1080i60": 67 | return DISP_TV_MOD_1080I_60HZ 68 | case "1080p24": 69 | return DISP_TV_MOD_1080P_24HZ 70 | case "1080p50": 71 | return DISP_TV_MOD_1080P_50HZ 72 | case "1080p": 73 | fallthrough 74 | case "1080p60": 75 | return DISP_TV_MOD_1080P_60HZ 76 | case "2160p": 77 | fallthrough 78 | case "2160p30": 79 | return DISP_TV_MOD_3840_2160P_30HZ 80 | case "2160p25": 81 | return DISP_TV_MOD_3840_2160P_25HZ 82 | case "2160p24": 83 | return DISP_TV_MOD_3840_2160P_24HZ 84 | } 85 | } 86 | 87 | return DISP_TV_MODE_UNKOWN 88 | } 89 | -------------------------------------------------------------------------------- /fb/fb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package fb 6 | 7 | const ( 8 | FB_ACTIVATE_ALL = 64 9 | 10 | FBIOGET_VSCREENINFO = 0x4600 11 | FBIOPUT_VSCREENINFO = 0x4601 12 | ) 13 | 14 | type BitField struct { 15 | offset uint32 16 | length uint32 17 | msb_right uint32 18 | } 19 | 20 | type VarScreenInfo struct { 21 | xres uint32 22 | yres uint32 23 | xres_virtual uint32 24 | yres_virtual uint32 25 | xoffset uint32 26 | yoffset uint32 27 | 28 | bits_per_pixel uint32 29 | grayscale uint32 30 | 31 | red BitField 32 | green BitField 33 | blue BitField 34 | transp BitField 35 | 36 | nonstd uint32 37 | 38 | activate uint32 39 | 40 | height uint32 41 | width uint32 42 | 43 | accel_flags uint32 44 | 45 | pixclock uint32 46 | left_margin uint32 47 | right_margin uint32 48 | upper_margin uint32 49 | lower_margin uint32 50 | hsync_len uint32 51 | vsync_len uint32 52 | sync uint32 53 | vmode uint32 54 | reserved [6]uint32 55 | } 56 | 57 | func (info *VarScreenInfo) SetXRes(xres, xres_virtual uint32) { 58 | info.xres = xres 59 | info.xres_virtual = xres_virtual 60 | } 61 | 62 | func (info *VarScreenInfo) SetYRes(yres, yres_virtual uint32) { 63 | info.yres = yres 64 | info.yres_virtual = yres_virtual 65 | } 66 | -------------------------------------------------------------------------------- /fb/fbset.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package fb 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "syscall" 11 | "unsafe" 12 | ) 13 | 14 | type Set struct { 15 | f *os.File 16 | } 17 | 18 | func NewSet(fn string) (*Set, error) { 19 | f, err := os.OpenFile(fn, os.O_RDWR, 0) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return &Set{ 25 | f: f, 26 | }, nil 27 | } 28 | 29 | func (set *Set) Close() { 30 | set.f.Close() 31 | } 32 | 33 | func (set *Set) GetVarScreenInfo() (*VarScreenInfo, error) { 34 | varScreenInfo := &VarScreenInfo{} 35 | _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, set.f.Fd(), FBIOGET_VSCREENINFO, uintptr(unsafe.Pointer(varScreenInfo))) 36 | if errno != 0 { 37 | return nil, fmt.Errorf("ioctl get_vscreeninfo failed %v", errno) 38 | } 39 | 40 | return varScreenInfo, nil 41 | } 42 | 43 | func (set *Set) SetVarScreenInfo(varScreenInfo *VarScreenInfo) error { 44 | varScreenInfo.activate = FB_ACTIVATE_ALL 45 | 46 | _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, set.f.Fd(), FBIOPUT_VSCREENINFO, uintptr(unsafe.Pointer(varScreenInfo))) 47 | if errno != 0 { 48 | return fmt.Errorf("ioctl put_vscreeninfo failed %v", errno) 49 | } 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /kernel/cmdline.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package kernel 6 | 7 | import ( 8 | "io/ioutil" 9 | "strings" 10 | ) 11 | 12 | const ( 13 | ProcCmdLine = "/proc/cmdline" 14 | ) 15 | 16 | func GetCmdlineParamValue(name string) (string, bool) { 17 | if raw, err := ioutil.ReadFile(ProcCmdLine); err == nil { 18 | params := strings.Split(string(raw), " ") 19 | 20 | for _, param := range params { 21 | if strings.HasPrefix(param, name) { 22 | value := strings.SplitN(param, "=", 2)[1] 23 | return value, true 24 | } 25 | } 26 | } 27 | 28 | return "", false 29 | } 30 | -------------------------------------------------------------------------------- /sunxi-disp-tool/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Simon Eisenmann . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | "strings" 12 | 13 | "github.com/longsleep/sunxi-disp-tool/disp2" 14 | "github.com/longsleep/sunxi-disp-tool/kernel" 15 | ) 16 | 17 | func usage() { 18 | fmt.Println("Usage: sunxi-disp-tool [] []") 19 | fmt.Println("Global options:") 20 | fmt.Println(" -screen int") 21 | fmt.Println(" Screen ID (default 0)") 22 | fmt.Println("Available commands are:") 23 | fmt.Println(" switch Switch HDMI output mode") 24 | fmt.Println(" init Initialize HDMI output mode from Kernel args") 25 | } 26 | 27 | func main() { 28 | globalOptions := flag.NewFlagSet("global", flag.ContinueOnError) 29 | screenID := globalOptions.Int("screen", 0, "Screen ID") 30 | 31 | switchCommand := flag.NewFlagSet("switch", flag.ExitOnError) 32 | outputMode := switchCommand.Int("mode", disp2.DISP_TV_MOD_1080P_60HZ, "Set HDMI output mode by number") 33 | outputModeName := switchCommand.String("name", "", "Set HDMI output mode by name") 34 | 35 | initCommand := flag.NewFlagSet("init", flag.ExitOnError) 36 | initKernelArg := initCommand.String("kernelarg", "disp.screen0_output_mode", "Set HDMI output mode from Kernel arg") 37 | 38 | globalOptions.Parse(os.Args[1:]) 39 | args := globalOptions.Args() 40 | 41 | if len(args) == 0 { 42 | usage() 43 | return 44 | } 45 | 46 | switch args[0] { 47 | case "switch": 48 | switchCommand.Parse(args[1:]) 49 | case "init": 50 | initCommand.Parse(args[1:]) 51 | case "-h": 52 | usage() 53 | return 54 | default: 55 | fmt.Printf("%q is not valid command.\n", args[0]) 56 | os.Exit(1) 57 | } 58 | 59 | disp, err := disp2.New() 60 | if err != nil { 61 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 62 | os.Exit(2) 63 | 64 | } 65 | defer disp.Close() 66 | 67 | // Read screen output type. 68 | outputType, err := disp.GetOutputType(*screenID) 69 | if err != nil { 70 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 71 | os.Exit(3) 72 | } 73 | 74 | // Only support HDMI for now. 75 | if outputType != disp2.DISP_OUTPUT_TYPE_HDMI { 76 | fmt.Fprintf(os.Stderr, "Screen is not HDMI - doing nothing.\n") 77 | return 78 | } 79 | 80 | if switchCommand.Parsed() { 81 | if *outputModeName != "" { 82 | mode := disp2.GetTVModFromString(*outputModeName) 83 | if mode != disp2.DISP_TV_MODE_UNKOWN { 84 | *outputMode = mode 85 | } 86 | } 87 | 88 | err = disp.Switch(*screenID, outputType, uint64(*outputMode)) 89 | } else if initCommand.Parsed() { 90 | mode := disp2.DISP_TV_MODE_UNKOWN 91 | if *initKernelArg != "" { 92 | boot, ok := kernel.GetCmdlineParamValue(*initKernelArg) 93 | if ok { 94 | resolutions := strings.Split(boot, ":") 95 | mode = disp2.GetTVModFromString(resolutions...) 96 | } 97 | } 98 | 99 | if mode == disp2.DISP_TV_MODE_UNKOWN { 100 | // No or invalid mode set via kernel parameter. Do nothing. 101 | return 102 | } 103 | 104 | err = disp.Switch(*screenID, outputType, uint64(mode)) 105 | } 106 | 107 | if err != nil { 108 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 109 | os.Exit(3) 110 | } 111 | } 112 | --------------------------------------------------------------------------------