blob: 9299b1124b8e2ddf0eafe61f1a559af53ef20615 [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* HPD Control Utility.
*
* This is a command-line tool running on Chameleon board to control the
* HPD signal, like emulating a plug, an unplug, or multiple HPD pulse.
*/
static const char *USAGE =
"command\n"
"\n"
"Commands:\n"
" status OFFSET - Shows the HPD status.\n"
" plug OFFSET - Assert HPD line to high, emulating a plug.\n"
" unplug OFFSET - Deassert HPD line to low, emulating an unplug.\n"
" repeat_pulse OFFSET TD TA C EL \n"
" - Repeat multiple HPD pulse (L->H->L->...->L->H).\n"
" TD: The time in usec of the deassert pulse.\n"
" TA: The time in usec of the assert pulse.\n"
" C: The repeat count.\n"
" EL: End level: 0 for LOW or 1 for HIGH.\n"
" pulse OFFSET W[0] W[1] ... \n"
" - Generate HPD pulses, starting at LOW, of mixed widths.\n"
" W[n]: segment widths in usec. W[0] is the width of the\n"
" first LOW segment; W[1] is that of the first HIGH\n"
" segment, W[2] is that of the second LOW segment, etc.\n"
" If even number of segments are specified, then the\n"
" HPD line stops at LOW; otherwise, it stops at HIGH.\n"
"\n"
"OFFSET:\n"
" DP1: 4\n"
" DP2: 8\n"
" HDMI: 12\n";
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <hpd_control.h>
/* The time in usec. If required HPD pulse is smaller than this duration.
* Set the process scheduling to the highest real-time priority.
*/
#define DURATION_NEED_RT 50000 // 50 msec
#define MAX_SEGMENT_COUNT 20 // for pulses of mixed widths
/* Commands and their dispatch functions */
static const struct cmd COMMAND_LIST[] = {
{ "status", cmd_status, 0 },
{ "plug", cmd_plug, 0 },
{ "unplug", cmd_unplug, 0 },
{ "repeat_pulse", cmd_repeat_pulse, 4 },
{ "pulse", cmd_pulse, 1 },
{ NULL, NULL, 0 } // end-of-list
};
/* Pointer to the first argument, i.e. the program name. */
static const char *g_argv0 = NULL;
/* Global varible for accessing the GPIO register */
static unsigned char *g_gpio_ptr = NULL;
/* Global varible for HPD offset in g_gpio_ptr */
static unsigned int hpd_offset = 0;
/* Prints the usage. */
static void usage(void)
{
fprintf(stderr, "Usage: %s %s", g_argv0, USAGE);
}
/* Initializes the memory map for the gpio register. */
void init(void)
{
int fd;
size_t page_size = sysconf(_SC_PAGESIZE);
unsigned char *mmap_addr;
// Memory-map the page which contains the GPIO register.
fd = open(MEM_DEV_FILE, O_RDWR | O_SYNC);
mmap_addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, MEM_ADDR_GPIO / page_size * page_size);
if (mmap_addr == (void *)-1) {
perror("mmap");
exit(EXIT_FAILURE);
}
// Add the offset in the page to address the GPIO register.
g_gpio_ptr = mmap_addr + MEM_ADDR_GPIO % page_size;
}
/* Sets the current process to run in the highest real-time priority. */
static void set_rt_scheduler(void)
{
struct sched_param sp;
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (sched_setscheduler(getpid(), SCHED_FIFO, &sp) != 0) {
perror("sched_setscheduler");
exit(EXIT_FAILURE);
}
}
/* Function to show the HPD status */
int cmd_status(const int argc, const char **argv)
{
printf("HPD=%d\n", (g_gpio_ptr[hpd_offset] & BIT_HPD_MASK) ? 1 : 0);
return 0;
}
/* Function to assert HPD line to high, emulating a plug */
int cmd_plug(const int argc, const char **argv)
{
// Set to plug
g_gpio_ptr[hpd_offset] |= BIT_HPD_MASK;
return 0;
}
/* Function to deassert HPD line to low, emulating an unplug */
int cmd_unplug(const int argc, const char **argv)
{
// Clear to unplug.
g_gpio_ptr[hpd_offset] &= ~BIT_HPD_MASK;
return 0;
}
/* Function to repeat multiple HPD pulse (H->L->H->...->H) */
int cmd_repeat_pulse(const int argc, const char **argv)
{
int deassert_usec, assert_usec, count, end_level;
int i;
deassert_usec = atoi(argv[0]);
assert_usec = atoi(argv[1]);
count = atoi(argv[2]);
end_level = atoi(argv[3]);
if (deassert_usec <= 0 || assert_usec <= 0 || count <= 0 ||
(end_level != 0 && end_level != 1)) {
fprintf(stderr, "Wrong paramenters.\n\n");
usage();
return 1;
}
// Only set real-time scheduling when the duration is too short.
if (deassert_usec <= DURATION_NEED_RT || assert_usec <= DURATION_NEED_RT)
set_rt_scheduler();
for (i = 0; i < count; i++) {
g_gpio_ptr[hpd_offset] &= ~BIT_HPD_MASK;
usleep(deassert_usec);
g_gpio_ptr[hpd_offset] |= BIT_HPD_MASK;
usleep(assert_usec);
}
//End with HPD low
if (!end_level) {
g_gpio_ptr[hpd_offset] &= ~BIT_HPD_MASK;
}
return 0;
}
/* Function to generate HPD pulses, starting at L, of mixed widths */
int cmd_pulse(int argc, const char **argv)
{
int segments[MAX_SEGMENT_COUNT];
int i;
if (argc > MAX_SEGMENT_COUNT) {
fprintf(stderr, "exceed max segment count %d < %d\n", MAX_SEGMENT_COUNT,
argc);
return 1;
}
for (i = 0; i < argc; i++) {
int p = atoi(argv[i]);
if (p <= 0) {
fprintf(stderr, "zero/negative width is not allowed: %d\n", p);
return 1;
}
segments[i] = p;
}
for (i = 0; i < argc; ) {
g_gpio_ptr[hpd_offset] &= ~BIT_HPD_MASK;
usleep(segments[i++]);
g_gpio_ptr[hpd_offset] |= BIT_HPD_MASK;
if (i < argc) {
usleep(segments[i++]);
}
}
if (i % 2 == 0) {
g_gpio_ptr[hpd_offset] &= ~BIT_HPD_MASK;
}
return 0;
}
/* Main program */
int main(const int argc, const char **argv)
{
const char *command = NULL;
const struct cmd *cur_cmd = NULL;
g_argv0 = argv[0];
// Print usage and quit if no argument command or no hpd_offset.
if (argc <= 2) {
usage();
return 1;
}
init();
command = argv[optind++];
hpd_offset = atoi(argv[optind++]);
if (hpd_offset != 4 && hpd_offset != 8 && hpd_offset != 12) {
fprintf(stderr, "Unsupported HPD offset: %u.\n\n", hpd_offset);
usage();
return 1;
}
// Hand off to the proper function.
for (cur_cmd = COMMAND_LIST; cur_cmd->name; ++cur_cmd) {
if (!strcmp(command, cur_cmd->name)) {
if (argc - optind < cur_cmd->argc) {
fprintf(stderr, "Expect at least %d parameters but got %d.\n\n",
cur_cmd->argc, argc - optind);
usage();
return 1;
}
return cur_cmd->func(argc - optind, &argv[optind]);
}
}
// No matched command. Print an error.
fprintf(stderr, "Unrecognized command.\n\n");
usage();
return 1;
}